remove tracker_global.c extern keyword to tracker_global.h
parent
abe9d0ccac
commit
5128a788fb
|
|
@ -0,0 +1,675 @@
|
||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
|
Copyright (C) 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 General Public License is a free, copyleft license for
|
||||||
|
software and other kinds of works.
|
||||||
|
|
||||||
|
The licenses for most software and other practical works are designed
|
||||||
|
to take away your freedom to share and change the works. By contrast,
|
||||||
|
the GNU General Public License is 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. We, the Free Software Foundation, use the
|
||||||
|
GNU General Public License for most of our software; it applies also to
|
||||||
|
any other work released this way by its authors. You can apply it to
|
||||||
|
your programs, too.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not
|
||||||
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
|
have the freedom to distribute copies of free software (and charge for
|
||||||
|
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.
|
||||||
|
|
||||||
|
To protect your rights, we need to prevent others from denying you
|
||||||
|
these rights or asking you to surrender the rights. Therefore, you have
|
||||||
|
certain responsibilities if you distribute copies of the software, or if
|
||||||
|
you modify it: responsibilities to respect the freedom of others.
|
||||||
|
|
||||||
|
For example, if you distribute copies of such a program, whether
|
||||||
|
gratis or for a fee, you must pass on to the recipients the same
|
||||||
|
freedoms that you received. You must make sure that they, too, receive
|
||||||
|
or can get the source code. And you must show them these terms so they
|
||||||
|
know their rights.
|
||||||
|
|
||||||
|
Developers that use the GNU GPL protect your rights with two steps:
|
||||||
|
(1) assert copyright on the software, and (2) offer you this License
|
||||||
|
giving you legal permission to copy, distribute and/or modify it.
|
||||||
|
|
||||||
|
For the developers' and authors' protection, the GPL clearly explains
|
||||||
|
that there is no warranty for this free software. For both users' and
|
||||||
|
authors' sake, the GPL requires that modified versions be marked as
|
||||||
|
changed, so that their problems will not be attributed erroneously to
|
||||||
|
authors of previous versions.
|
||||||
|
|
||||||
|
Some devices are designed to deny users access to install or run
|
||||||
|
modified versions of the software inside them, although the manufacturer
|
||||||
|
can do so. This is fundamentally incompatible with the aim of
|
||||||
|
protecting users' freedom to change the software. The systematic
|
||||||
|
pattern of such abuse occurs in the area of products for individuals to
|
||||||
|
use, which is precisely where it is most unacceptable. Therefore, we
|
||||||
|
have designed this version of the GPL to prohibit the practice for those
|
||||||
|
products. If such problems arise substantially in other domains, we
|
||||||
|
stand ready to extend this provision to those domains in future versions
|
||||||
|
of the GPL, as needed to protect the freedom of users.
|
||||||
|
|
||||||
|
Finally, every program is threatened constantly by software patents.
|
||||||
|
States should not allow patents to restrict development and use of
|
||||||
|
software on general-purpose computers, but in those that do, we wish to
|
||||||
|
avoid the special danger that patents applied to a free program could
|
||||||
|
make it effectively proprietary. To prevent this, the GPL assures that
|
||||||
|
patents cannot be used to render the program non-free.
|
||||||
|
|
||||||
|
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 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. Use with the GNU Affero General Public License.
|
||||||
|
|
||||||
|
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 Affero 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 special requirements of the GNU Affero General Public License,
|
||||||
|
section 13, concerning interaction through a network will apply to the
|
||||||
|
combination as such.
|
||||||
|
|
||||||
|
14. Revised Versions of this License.
|
||||||
|
|
||||||
|
The Free Software Foundation may publish revised and/or new versions of
|
||||||
|
the GNU 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 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 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 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 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 General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU 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 the program does terminal interaction, make it output a short
|
||||||
|
notice like this when it starts in an interactive mode:
|
||||||
|
|
||||||
|
<program> Copyright (C) <year> <name of author>
|
||||||
|
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||||
|
This is free software, and you are welcome to redistribute it
|
||||||
|
under certain conditions; type `show c' for details.
|
||||||
|
|
||||||
|
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||||
|
parts of the General Public License. Of course, your program's commands
|
||||||
|
might be different; for a GUI interface, you would use an "about box".
|
||||||
|
|
||||||
|
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 GPL, see
|
||||||
|
<http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
The GNU General Public License does not permit incorporating your program
|
||||||
|
into proprietary programs. If your program is a subroutine library, you
|
||||||
|
may consider it more useful to permit linking proprietary applications with
|
||||||
|
the library. If this is what you want to do, use the GNU Lesser General
|
||||||
|
Public License instead of this License. But first, please read
|
||||||
|
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
||||||
|
|
||||||
|
|
@ -0,0 +1,640 @@
|
||||||
|
|
||||||
|
Version 5.02 2014-06-23
|
||||||
|
* corect README spell mistake
|
||||||
|
* bug fixed: can't deal sync truncate file exception
|
||||||
|
* remove tracker_global.c extern keyword to tracker_global.h
|
||||||
|
|
||||||
|
Version 5.01 2014-02-02
|
||||||
|
* trunk binlog be compressed when trunk init
|
||||||
|
* bug fixed: sync trunk binlog file to other storage servers immediately when
|
||||||
|
the trunk server init done
|
||||||
|
* move ioevent_loop.[hc] and fast_task_queue.[hc] from tracker/ to common/
|
||||||
|
* hash table support locks
|
||||||
|
* hash talbe support new functions: hash_inc and hash_inc_ex
|
||||||
|
|
||||||
|
Version 5.00 2013-12-23
|
||||||
|
* discard libevent, use epoll in Linux, kqueue in FreeBSD, port in SunOS directly
|
||||||
|
* do_notify_leader_changed force close connection when target is myself
|
||||||
|
* modify the INSTALL file and tracker/Makefile.in
|
||||||
|
|
||||||
|
Version 4.08 2013-11-30
|
||||||
|
* bug fixed: FDFS_DOWNLOAD_SERVER_ROUND_ROBIN change to FDFS_STORE_SERVER_ROUND_ROBIN
|
||||||
|
* dio_init use memset to init buffer
|
||||||
|
* disable linger setting (setsockopt with option SO_LINGER)
|
||||||
|
* change log level from error to warning when file not exist on storage server
|
||||||
|
|
||||||
|
Version 4.07 2013-06-02
|
||||||
|
* make.sh add -lpthread by ldconfig check
|
||||||
|
* support multi accept threads
|
||||||
|
* tracker and storage server close client connection when recv invalid package
|
||||||
|
* client/storage_client.c: file_exist with silence flag
|
||||||
|
* tracker and storage process support start, stop and restart command
|
||||||
|
* tracker/tracker_proto.c fdfs_recv_header: logDebug change to logError
|
||||||
|
|
||||||
|
Version 4.06 2013-01-24
|
||||||
|
* fdfs_upload_file tool enhancement
|
||||||
|
* fdfs_download_file tool support offset and download size
|
||||||
|
* trunk file upload support sub paths rotating correctly
|
||||||
|
* add function: fdfs_http_get_file_extension
|
||||||
|
* sync truncate file operation anyway
|
||||||
|
|
||||||
|
Version 4.05 2012-12-30
|
||||||
|
* client/fdfs_upload_file.c can specify storage ip port and store path index
|
||||||
|
* add connection pool
|
||||||
|
* client load storage ids config
|
||||||
|
* common/ini_file_reader.c does NOT call chdir
|
||||||
|
* keep the mtime of file same
|
||||||
|
* use g_current_time instead of call time function
|
||||||
|
* remove embed HTTP support
|
||||||
|
|
||||||
|
Version 4.04 2012-12-02
|
||||||
|
* bug fixed: get storage server id when storage daemon init
|
||||||
|
* storage id in filename use global variable
|
||||||
|
* dynamic alloc memory 8 bytes alignment
|
||||||
|
* fast_task_queue support memory pool chain
|
||||||
|
|
||||||
|
Version 4.03 2012-11-18
|
||||||
|
* trunk_mgr/trunk_mem.c: log error and add more debug info
|
||||||
|
* file id generated by storage server can include storage server ID
|
||||||
|
|
||||||
|
Version 4.02 2012-10-30
|
||||||
|
* validate file_ext_name and prefix_name when upload file
|
||||||
|
* storage.conf add parameter: file_sync_skip_invalid_record
|
||||||
|
* add offset debug info when sync file fail
|
||||||
|
* bug fixed: log to binlog also if the file exists when sync file
|
||||||
|
* tracker and storage error log support rotate
|
||||||
|
* support rotate log by file size
|
||||||
|
* rotate log when receive HUP signal
|
||||||
|
* fdfs_monitor support set trunk server
|
||||||
|
* bug fixed: tracker_mem.c correct double mutex lock
|
||||||
|
|
||||||
|
Version 4.01 2012-10-21
|
||||||
|
* trunk_mgr/trunk_mem.c: trunk init flag check more strictly
|
||||||
|
* file signature for checking file duplicate support MD5
|
||||||
|
* slave file support both symbol link and direct file
|
||||||
|
* tracker server log trunk server change logs
|
||||||
|
|
||||||
|
Version 4.00 2012-10-06
|
||||||
|
* identify storage server by ID instead of IP address
|
||||||
|
* tracker.conf: storage reserved space can use ratio such as 10%
|
||||||
|
* storage server support access log
|
||||||
|
* appender file and trunk file also use rand number in file id
|
||||||
|
* bug fixed: test_upload.c: char file_id[64] change to: char file_id[128]
|
||||||
|
* set pipe reading fd with attribute O_NOATIME
|
||||||
|
* bug fixed: correct php extension call_user_function TSRMLS_DC with TSRMLS_CC
|
||||||
|
|
||||||
|
Version 3.11 2012-08-04
|
||||||
|
* setsockopt set linger.l_linger to micro-seconds in FreeBSD and seconds
|
||||||
|
in others
|
||||||
|
* trunk binlog reader skip incorrect records
|
||||||
|
* bug fixed: single disk recovery support symbol link and trunk file
|
||||||
|
* storage generate filename enhancement
|
||||||
|
* ETIME change to ETIMEDOUT for FreeBSD
|
||||||
|
* tracker_mem.c: load storage server ignore empty ip address
|
||||||
|
|
||||||
|
Version 3.10 2012-07-22
|
||||||
|
* check and init trunk file more gracefully
|
||||||
|
* remove unused-but-set-variable
|
||||||
|
* bug fixed: return correct group name when g_check_file_duplicate is true
|
||||||
|
* bug fixed: php extension call_user_function replace TSRMLS_CC with TSRMLS_DC
|
||||||
|
* large the interval of tracker re-select trunk server
|
||||||
|
* trunk free block check duplicate using avl tree
|
||||||
|
* trunk file sync overwrite the dest file anyway
|
||||||
|
* common/avl_tree.c: free data when delete
|
||||||
|
* tracker.conf add parameter: trunk_init_reload_from_binlog, when this flag
|
||||||
|
is set to true, load all free trunk blocks from the trunk binlog
|
||||||
|
* trunk status control only by trunk_mem.c and memcmp struct FDFSTrunkFullInfo
|
||||||
|
avoid memory alignment problem
|
||||||
|
* auto remove the too old temp file
|
||||||
|
|
||||||
|
Version 3.09 2012-07-08
|
||||||
|
* make.sh avoid override config files of /etc/fdfs/
|
||||||
|
* common/logger.c: function log_init can be called more than once
|
||||||
|
* php extension logInfo change to logDebug
|
||||||
|
* c client logInfo change to logDebug
|
||||||
|
* storage_dio.c log info more properly
|
||||||
|
* delete the trunk space which be occupied
|
||||||
|
* tracker.conf add parameter: trunk_init_check_occupying, when this flag
|
||||||
|
is set to true, do not add the trunk nodes which be occupied
|
||||||
|
* another method to get local ip addresses
|
||||||
|
|
||||||
|
Version 3.08 2012-05-27
|
||||||
|
* FAST_MAX_LOCAL_IP_ADDRS change from 4 to 16
|
||||||
|
* appender file support modify
|
||||||
|
* appender file support truncate
|
||||||
|
|
||||||
|
Version 3.07 2012-05-13
|
||||||
|
* tracker/tracker_mem.c: check storage ip address is not empty
|
||||||
|
* remove direct IO support
|
||||||
|
* trunk binlog sync optimization
|
||||||
|
* php extension compile passed in PHP 5.4.0
|
||||||
|
* get local ip addresses enhancement
|
||||||
|
* trunk server select the storage server whose binglog file size is max
|
||||||
|
* sync trunk binlog file correctly when trunk server changed
|
||||||
|
|
||||||
|
Version 3.06 2012-01-22
|
||||||
|
* add common/avl_tree.h and common/avl_tree.c
|
||||||
|
* organize trunk free blocks using AVL tree
|
||||||
|
* find the trunk server for each group when current tracker be a leader
|
||||||
|
* common/sched_thread.c can add schedule entry dynamicly
|
||||||
|
* support creating trunk file advancely
|
||||||
|
|
||||||
|
Version 3.05 2011-12-20
|
||||||
|
* remove compile warnings
|
||||||
|
* storage server's store_path_count can be more than that of group
|
||||||
|
* bug fixed: common/fast_mblock.c malloc bytes are not enough
|
||||||
|
* make.sh support OS: HP-UX
|
||||||
|
|
||||||
|
Version 3.04 2011-11-25
|
||||||
|
* bug fixed: duplicate files only save one entry ok with trunk file mode
|
||||||
|
* bug fixed: sync correctly with more binlog files
|
||||||
|
* fdfs_file_info query file info from storage server
|
||||||
|
* bug fixed: php extension compile error using gcc 4.6.1 as:
|
||||||
|
variable 'store_path_index' set but not used
|
||||||
|
* bug fixed: delete the metadata of trunked file correctly
|
||||||
|
* bug fixed: append file ok when check duplicate is on
|
||||||
|
* storage/trunk_mgr/trunk_shared.[hc]: trunk_file_stat_func do not
|
||||||
|
use function pointer
|
||||||
|
* bug fixed: storage/trunk_mgr/trunk_shared.c base64_decode_auto
|
||||||
|
overflow 1 byte
|
||||||
|
* bug fixed: delete slave file correctly
|
||||||
|
* bug fixed: remove debug info
|
||||||
|
* md5 function name changed to avoid conflict
|
||||||
|
|
||||||
|
Version 3.03 2011-10-16
|
||||||
|
* ignore existed link when sync link file
|
||||||
|
* http token checking support persistent token
|
||||||
|
* add functions: storage_file_exist and storage_file_exist1
|
||||||
|
* php minfo add fastdfs version info
|
||||||
|
* make.sh changed
|
||||||
|
* client move libevent dependency
|
||||||
|
|
||||||
|
Version 3.02 2011-09-18
|
||||||
|
* bug fixed: tracker_mem_check_add_tracker_servers add tracker server
|
||||||
|
correctly
|
||||||
|
* php client compile ok with php 5.2.17
|
||||||
|
* re-select trunk server ok
|
||||||
|
|
||||||
|
Version 3.01 2011-07-31
|
||||||
|
* bug fixed: tracker_get_connection_ex and tracker_get_connection_r_ex
|
||||||
|
connect two times with multi tracker servers
|
||||||
|
* bug fixed: tracker_mem_check_add_tracker_servers condition not correct
|
||||||
|
* all logError add source filename and line
|
||||||
|
* php extension support upload file callback
|
||||||
|
* php extension support download file callback
|
||||||
|
|
||||||
|
Version 3.00 2011-06-19
|
||||||
|
* mass small files optimization
|
||||||
|
* add fixed block memory pool: common/fast_mblock.c
|
||||||
|
* bug fixed: tracker_mem.c do NOT clear g_groups fields
|
||||||
|
* bug fixed: slave file and appender file download ok
|
||||||
|
* bug fixed: tracker / storage run by group / user, set file owner
|
||||||
|
* tracker server support leader
|
||||||
|
* client support static library
|
||||||
|
* client_func.h add functions fdfs_tracker_group_equals and
|
||||||
|
fdfs_get_file_ext_name
|
||||||
|
* bug fixed: test/dfs_func_pc.c compile ok
|
||||||
|
* storage server check free space enough when upload a file
|
||||||
|
|
||||||
|
Version 2.09 2011-02-19
|
||||||
|
* bug fixed: write_to_binlog_index then increase g_binlog_index (feedback
|
||||||
|
by koolcoy)
|
||||||
|
* disk read / write supports direct mode (avoid caching by the file system)
|
||||||
|
|
||||||
|
Version 2.08 2011-01-30
|
||||||
|
* bug fixed: fdfs_trackerd.c set g_tracker_thread_count to 0
|
||||||
|
* add cmd TRACKER_PROTO_CMD_SERVER_LIST_ONE_GROUP to support list one group
|
||||||
|
* support disk recovery automatically
|
||||||
|
* support total_upload_bytes, success_upload_bytes, total_download_bytes and
|
||||||
|
success_download_bytes etc. 18 stat fields
|
||||||
|
* tracker data file storage_groups.dat changes to storage_groups_new.dat, and
|
||||||
|
storage_servers.dat changes to storage_servers_new.dat
|
||||||
|
* support file append, add tests: fdfs_appender_test and fdfs_appender_test1
|
||||||
|
* storage_dio.c: dio_deal_task split to several functions
|
||||||
|
* tracker http check thread exit normally
|
||||||
|
* function fdfs_get_file_info_ex changed, add function fdfs_get_file_info_ex1
|
||||||
|
* fix some type cast error when compile with c++
|
||||||
|
* client add tools: fdfs_upload_appender and fdfs_append_file
|
||||||
|
|
||||||
|
Version 2.07 2011-01-09
|
||||||
|
* slave file's prefix name can be empty
|
||||||
|
* FDFS_MAX_GROUPS change from 64 to 512
|
||||||
|
* file size field in the file id changed: high 32 bits is random integer
|
||||||
|
when the file size < 2GB and the highest bit set to 1
|
||||||
|
* tracker_service.c: in function list_group_storages, use strcpy
|
||||||
|
intead of memcpy
|
||||||
|
* php extension add function fastdfs_tracker_delete_storage
|
||||||
|
* client add tool: fdfs_file_info to get file info, including file size,
|
||||||
|
create timestamp, source storage ip address and crc32 signature
|
||||||
|
* fdfs_upload_file.c: omit more error info when the local file not exist
|
||||||
|
|
||||||
|
Version 2.06 2010-12-26
|
||||||
|
* sync file op: do not sync the file which exists on dest storage server
|
||||||
|
and the file size are same
|
||||||
|
* bug fixed: sync copy file will clear the existed file on dest storage
|
||||||
|
server (truncate the file size to 0), this bug caused by V2.04
|
||||||
|
* bug fixed: make temp file discard system function mkstemp,
|
||||||
|
use file sequence No. with pthread_mutex_lock
|
||||||
|
* bug fixed: function fastdfs_tracker_list_groups, when parameter group_name
|
||||||
|
is null or empty string, return all groups info
|
||||||
|
* bug fixed: upload a file extends 2GB will fail
|
||||||
|
* bug fixed: tracker to tracker sync system data files, in function:
|
||||||
|
tracker_mem_get_tracker_server, pTrackerStatus not be set properly
|
||||||
|
|
||||||
|
Version 2.05 2010-12-05
|
||||||
|
* client/fdfs_monitor.c: add sync delay time
|
||||||
|
* tracker/fast_task_queue.c: pTask->data = pTask->arg + arg_size;
|
||||||
|
change to: pTask->data = (char *)pTask->arg + arg_size;
|
||||||
|
* bug fixed: storage_sync.c line 237 cause core dump in Ubuntu 10.04
|
||||||
|
* upload file test use mmap, support more test_upload processes
|
||||||
|
* client add three tools: fdfs_upload_file, fdfs_download_file and
|
||||||
|
fdfs_delete_file
|
||||||
|
|
||||||
|
Version 2.04 2010-11-19
|
||||||
|
* storage.conf: tracker server ip can NOT be 127.0.0.1
|
||||||
|
* do not catch signal SIGABRT
|
||||||
|
* strerror change to STRERROR macro
|
||||||
|
* sync copy file use temp filename first, rename to the correct filename
|
||||||
|
when sync done
|
||||||
|
* file id use 4 bytes CRC32 signature instead of random number
|
||||||
|
* add file: client/fdfs_crc32.c
|
||||||
|
* one of file hash code signature function change from APHash_ex
|
||||||
|
to simple_hash_ex
|
||||||
|
* bug fixed: when fdfs_storaged quit, maybe write to binlog file fail,
|
||||||
|
the error info is "Bad file descriptor"
|
||||||
|
|
||||||
|
Version 2.03 2010-11-08
|
||||||
|
* bug fixed: core dump when http.need_find_content_type=false and
|
||||||
|
http.anti_steal.check_token=true
|
||||||
|
* storage server add join_time field (create timestamp of this storage)
|
||||||
|
* tracker server fetch system files from other tracker server when
|
||||||
|
first storage server join in (tracker to tracker sync system files)
|
||||||
|
* tracker server changes the old ip address to the new address when the
|
||||||
|
storage server ip address changed
|
||||||
|
* tracker to tracker sync system data files in some case, multi tracker
|
||||||
|
server supported well
|
||||||
|
|
||||||
|
Version 2.02 2010-10-28
|
||||||
|
* get parameters function from tracker server changed,
|
||||||
|
add paramter: storage_sync_file_max_delay
|
||||||
|
* local ip functions move to common/local_ip_func.c
|
||||||
|
* when query all storage servers to store, do not increase the current
|
||||||
|
write server index
|
||||||
|
* struct FDFSHTTPParams add field: need_find_content_type
|
||||||
|
* symbol link client library to /usr/lib64 in 64 bits OS
|
||||||
|
* storage_client.c: deal file extension name correctly
|
||||||
|
|
||||||
|
Version 2.01 2010-10-17
|
||||||
|
* client/fdfs_monitor.c can specify tracker server
|
||||||
|
* micro STORAGE_STORE_PATH_PREFIX_CHAR change to
|
||||||
|
FDFS_STORAGE_STORE_PATH_PREFIX_CHAR
|
||||||
|
* php extension can set log filename
|
||||||
|
* php extension add function: fastdfs_client_version
|
||||||
|
* bug fixed: client/tracker_client.c tracker_get_connection_ex NULL pointer
|
||||||
|
* set max core dump file size to at least 256MB when DEBUG_FLAG is on,
|
||||||
|
make sure to generate core file when core dump with DEBUG_FLAG on
|
||||||
|
* upload file can get available storage server list of the group,
|
||||||
|
add command TRACKER_PROTO_CMD_SERVICE_QUERY_STORE_WITHOUT_GROUP_ALL and
|
||||||
|
TRACKER_PROTO_CMD_SERVICE_QUERY_STORE_WITH_GROUP_ALL
|
||||||
|
* bug fixed: storage core dump in some case
|
||||||
|
|
||||||
|
Version 2.00 2010-08-22
|
||||||
|
* tracker network io use libevent instead of traditional io model
|
||||||
|
* storage network io use libevent instead of traditional io model
|
||||||
|
* storage disk read/write use separate threads
|
||||||
|
* tracker_mem.c malloc single group and storage struct, remove referer
|
||||||
|
* make install copy config files
|
||||||
|
* tracker.conf add two parameters: storage_sync_file_max_delay and
|
||||||
|
storage_sync_file_max_time
|
||||||
|
* client tracker_get_connection increase server_index correctly
|
||||||
|
* storage sync to storage server adds active test
|
||||||
|
* test programs compile ok
|
||||||
|
|
||||||
|
Version 1.29 2010-06-30
|
||||||
|
* add files: tracker_dump.h and tracker_dump.c, tracker dump global vars
|
||||||
|
* add files: storage_dump.h and storage_dump.c, storage dump global vars
|
||||||
|
* sockopt.c: tcprecvfile and tcpdiscard add parameter total_recv_bytes
|
||||||
|
* storage server add fields: storage_port and storage_http_port
|
||||||
|
* auto rename synced remark files when the port of all storage servers
|
||||||
|
in a group changed to another port
|
||||||
|
* connect server support timeout, adding connect_timeout parameter in
|
||||||
|
config file
|
||||||
|
* log_init set log to cache to false (no cache)
|
||||||
|
|
||||||
|
Version 1.28 2010-05-30
|
||||||
|
* tracker_servive.c: set current_write_group anyway when current group
|
||||||
|
out of space
|
||||||
|
* logger support context (multi instance)
|
||||||
|
* get storage servers by filename: if the file created one day ago (the create
|
||||||
|
timestamp of the file < current_time - 86400), any active storage server matches
|
||||||
|
* add files: common/pthread_func.h and common/pthread_func.c
|
||||||
|
* common/sched_thread.h, remove statement: extern bool g_continue_flag;
|
||||||
|
* client add libfastcommon
|
||||||
|
* global variables: g_base_path, g_network_timeout, g_version change to
|
||||||
|
g_fdfs_base_path, g_fdfs_network_timeout, g_fdfs_version
|
||||||
|
* common/fdfs_base64.h/c change name to common/base64.h/c
|
||||||
|
* make.sh use TARGET_PREFIX instead of TARGET_PATH
|
||||||
|
* protocol add ACTIVE_TEST, tracker and storage both support
|
||||||
|
* php client, bug fixed: fastdfs_connect_server, the sock must init to -1
|
||||||
|
* bug fixed: storage status not correct with multi tracker servers
|
||||||
|
* sync storage mark file and stat file to disk properly
|
||||||
|
|
||||||
|
Version 1.27 2010-04-10
|
||||||
|
* storage.conf: add if_alias_prefix parameter to get the ip address of the
|
||||||
|
local host
|
||||||
|
* storage http support domain name
|
||||||
|
* php extension add some parameters in fastdfs_client.ini
|
||||||
|
* make.sh compile use debug mode
|
||||||
|
* type off_t change to int64_t
|
||||||
|
* redirect stdout and stderr to log file
|
||||||
|
* php extension list_groups add fields: version and http_domain
|
||||||
|
|
||||||
|
Version 1.26 2010-02-28
|
||||||
|
* remove compile warning of logError
|
||||||
|
* ini reader support section
|
||||||
|
* bug fixed: tracker/tracker_mem.c sync storage server status
|
||||||
|
* use storage server http server port anyway
|
||||||
|
* bug fixed: ini reader can support relative config filename
|
||||||
|
* function enhancement: tracker server can check storage HTTP server alive
|
||||||
|
|
||||||
|
Version 1.25 2010-02-04
|
||||||
|
* storage_sync.c if source file not exist when sync a file, change from
|
||||||
|
logWarning to logDebug
|
||||||
|
* filename buff size change from 64 to 128
|
||||||
|
* bug fixed: c client and php client, log not inited cause core dump when
|
||||||
|
call log functions
|
||||||
|
* can print stack trace when process core dumped in Linux server
|
||||||
|
* bug fixed: tracker/tracker_mem.c load storage servers fail with many groups
|
||||||
|
and storage servers
|
||||||
|
* common/sockopt.c remove debug info
|
||||||
|
* storage stat add fields: version
|
||||||
|
* auto adjust when storage server ip address changed
|
||||||
|
* bug fixed: when add a new storage server, other storage servers' status keep
|
||||||
|
the same, not changed
|
||||||
|
* add macros, compile passed in cygwin, thanks Seapeak
|
||||||
|
* write to system data file using lock
|
||||||
|
* common/ini_file_reader.c: use one context parameter, not two parameters
|
||||||
|
* storage status sync modified (the code of tracker and storage both changed)
|
||||||
|
* when recv kill signal, worker thread quit more quickly, daemon process
|
||||||
|
fdfs_trackerd and fdfs_storage quit very quickly when recv kill signal
|
||||||
|
* remove compile warning info of logError
|
||||||
|
* tracker server start more quickly with many groups and storage servers
|
||||||
|
* bug fixed: correct off_t printf format
|
||||||
|
|
||||||
|
Version 1.24 2010-01-06
|
||||||
|
* call php_fdfs_close with TSRMLS_CC as php_fdfs_close(i_obj TSRMLS_CC)
|
||||||
|
* storage server to storage server report ip address as tracker client
|
||||||
|
* bug fixed: sendfile exceeds 2GB file in Linux
|
||||||
|
* bug fixed: delete storage server
|
||||||
|
* storage stat add fields: up_time and src_ip_addr
|
||||||
|
* big static or struct memeber char array buffer change to malloc in order to
|
||||||
|
decrease stack size
|
||||||
|
* FDFS_WRITE_BUFF_SIZE change from 512KB to 256KB
|
||||||
|
* bug fixed: client/storage_client.c, meta data miss when upload file
|
||||||
|
* decrease thread_stack_size default value in config files: tracker.conf
|
||||||
|
and storage.conf
|
||||||
|
|
||||||
|
Version 1.23 2009-11-29
|
||||||
|
* remove unuseless variable "sleep_secs" in tracker_report_thread_entrance
|
||||||
|
* storage can bind an address when connect to other servers (as a client)
|
||||||
|
* common/md5.h fix UINT4 typedef wrong type in 64 bit OS
|
||||||
|
* client/fdfs_test.c: print the source ip address decoded from the remote
|
||||||
|
filename
|
||||||
|
* client add function fdfs_get_file_info
|
||||||
|
* php extension add functions: fastdfs_http_gen_token and fastdfs_get_file_info
|
||||||
|
* server process will exit when the http service starts fail
|
||||||
|
* support file group, a master file with many slave files whose file id can be
|
||||||
|
combined from master file id and prefix
|
||||||
|
* php client support uploading slave file
|
||||||
|
* ip address in filename change from host byte order to network byte order
|
||||||
|
* storage sync performance enhancement, using read buffer of 64KB to avoid
|
||||||
|
reading binlog file repeatly
|
||||||
|
* storage add prototol cmd: STORAGE_PROTO_CMD_QUERY_FILE_INFO
|
||||||
|
* FDFS_FILE_EXT_NAME_MAX_LEN changed from 5 to 6
|
||||||
|
* get file info support slave file
|
||||||
|
* storage server for uploading file support priority
|
||||||
|
|
||||||
|
Version 1.22 2009-10-12
|
||||||
|
* bug fixed: common/shared_func.c allow_hosts array maybe overflow in some case
|
||||||
|
* tracker/tracker_mem.c: protocol TRACKER_PROTO_CMD_SERVICE_QUERY_FETCH_ALL,
|
||||||
|
return at least a storage server when active storage
|
||||||
|
server count of the group > 0
|
||||||
|
* bug fixed: when client connection disconnected, always log debug or error info
|
||||||
|
* make.sh: default not install FastDFS services in Linux server
|
||||||
|
* common/sockopt.c: setsockopt level SOL_TCP only supported in Linux
|
||||||
|
* common/http_func.c: do not use function strsep because strsep is not portable
|
||||||
|
* client upload file support callback function
|
||||||
|
* client support multi tracker groups (multi FastDFS clusters)
|
||||||
|
* bug fixed: thread_stack_size not correct when the param thread_stack_size
|
||||||
|
not set in the config file
|
||||||
|
* supply php extension (directory name: php_client)
|
||||||
|
* c client reconnect server (tracker or storage) when network IO error
|
||||||
|
* c client: make tracker server index counter thread safely
|
||||||
|
|
||||||
|
Version 1.21 2009-09-19
|
||||||
|
* bug fixed: when source storage server synced file to new storage server done,
|
||||||
|
it's status changed to ONLINE (should keep as ACTIVE, report by zhouzezhong)
|
||||||
|
* add thread_stack_size in config file, default value is 1MB (report by chhxo)
|
||||||
|
* tracker and storage server use setsockopt to keep alive
|
||||||
|
(report by zhouzezhong)
|
||||||
|
* bug fixed: storage server with multi-path, upload file fail when the free
|
||||||
|
space of each path <= reserved space (the total free space > reserved space,
|
||||||
|
report by zhouzezhong)
|
||||||
|
* storage_sync.c: when connect fail, do not change the dest storage server '
|
||||||
|
status to offline
|
||||||
|
* tracker_service.c and storage_service.c change log level from WARNING to DEBUG
|
||||||
|
when client connection disconnected (report by Jney402)
|
||||||
|
* bug fixed: tracker_client.c correct store_path_index return by tracker server
|
||||||
|
(report by happy_fastdfs)
|
||||||
|
* bug fixed: tracker_service.c when store_lookup set to 2 (load balance), use
|
||||||
|
another pthread lock to avoid long time lock waiting
|
||||||
|
(report by happy_fastdfs)
|
||||||
|
* add service shell scripts in directory: init.d
|
||||||
|
(services will auto installed on Linux, report by hugwww)
|
||||||
|
|
||||||
|
Version 1.20 2009-09-05
|
||||||
|
* base64 use context, functions changed
|
||||||
|
* common/ini_file_reader.c: fix memory leak
|
||||||
|
* tracker server support HTTP protocol, one thread mode
|
||||||
|
* storage server support HTTP protocol, one thread mode
|
||||||
|
* fix bug: storage server rebuild, auto sync data correctly
|
||||||
|
* fix bug: sync data fail (correct storage server status)
|
||||||
|
* when storage server idle time exceeds check_active_interval seconds,
|
||||||
|
set it's status to offline
|
||||||
|
* tracker counter thread safely
|
||||||
|
|
||||||
|
Version 1.19 2009-07-23
|
||||||
|
* use poll instead of select in sockopt.c
|
||||||
|
* hash.c use chain impl by self
|
||||||
|
* use FastDHT 1.09 client code
|
||||||
|
* ini reader support HTTP protocol, conf file can be an url
|
||||||
|
* correct test dir compile error
|
||||||
|
* use non-block socket to increase network IO performance
|
||||||
|
* add cmd TRACKER_PROTO_CMD_SERVICE_QUERY_FETCH_ALL: query all storage servers
|
||||||
|
from which the file can be dowloaded
|
||||||
|
* while (1) ... break; changed to do ... while (0);
|
||||||
|
|
||||||
|
Version 1.18 2009-05-24
|
||||||
|
* restart.sh only kill the programs match the program name and all parameters
|
||||||
|
* correct get local ip addresses
|
||||||
|
* common files do not use global vars like g_network_timeout and g_base_path
|
||||||
|
* download file support offset and download bytes
|
||||||
|
* hash function change type from unsigned int to signed int
|
||||||
|
* file size in file name support 64 bits, old bytes is 4, new bytes is 8
|
||||||
|
|
||||||
|
Version 1.17 2009-03-19
|
||||||
|
* add test programs at sub directory test/
|
||||||
|
* common/shared_func.c: rindex change to strrchr, add #include <netinet/in.h>
|
||||||
|
* support SunOS (Solaris), compile passed on SunOS 5.10
|
||||||
|
* support AIX, compile passed on AIX 5.3
|
||||||
|
* sys call statfs change to statvfs
|
||||||
|
* use scheduling thread to sync binlog buff / cache to disk, add parameter
|
||||||
|
"sync_binlog_buff_interval" to conf file storage.conf
|
||||||
|
* use FastDHT v1.07 client code
|
||||||
|
|
||||||
|
Version 1.16 2009-02-14
|
||||||
|
* client can specify group name when upload file
|
||||||
|
* tracker_service.c: cmd dispatch changed to "switch ... case"
|
||||||
|
not "if ... else if"
|
||||||
|
* storage_service.c: call fdfs_quit before tracker_disconnect_server
|
||||||
|
|
||||||
|
Version 1.15 2009-01-28
|
||||||
|
* use FastDHT v1.04 client code
|
||||||
|
* use FastDHT client thread safely
|
||||||
|
|
||||||
|
Version 1.14 2009-01-18
|
||||||
|
* storage/storage_sync.c:
|
||||||
|
old: if (reader.sync_row_count % 1000 == 0)
|
||||||
|
new: if (reader.scan_row_count % 2000 == 0)
|
||||||
|
* little adjustment for common files can be used by FastDHT
|
||||||
|
* sched_thread.h /.c add global variable g_schedule_flag to quit normally
|
||||||
|
* shared_func.h / .c add function get_time_item_from_conf
|
||||||
|
* sched_thread.h /.c support time_base of task
|
||||||
|
* hash.h / .c add function CRC32, add hash function to support stream hash
|
||||||
|
* add FastDHT client files in storage/fdht_client/
|
||||||
|
* create symbol link when the file content is duplicate,
|
||||||
|
add item "check_file_duplicate" to conf file storage.conf
|
||||||
|
* use FastDHT v1.02 client code
|
||||||
|
* auto delete invalid entry in FastDHT when the source file does not exist
|
||||||
|
|
||||||
|
Version 1.13 2008-11-29
|
||||||
|
* re-calculate group 's free space when one of it's storage servers'
|
||||||
|
free space increase
|
||||||
|
* add parameters: sync_interval, sync_start_time and sync_end_time to
|
||||||
|
storage.conf
|
||||||
|
* performance enhancement: log to buffer, flush to disk every interval seconds
|
||||||
|
* standard fds closed by daemon_init: 0(stdin), 1(stdout) and 2(stderr)
|
||||||
|
* fix bug: pthread_kill sometimes cause core dump when program terminated
|
||||||
|
* fix bug: sync.c open next binlog cause loop call
|
||||||
|
|
||||||
|
Version 1.12 2008-11-12
|
||||||
|
* storage server support multi path (mount point)
|
||||||
|
* upload file support file ext name, add source storage ip address to filename
|
||||||
|
* add delete command to delete the invalid storage server
|
||||||
|
* add client functions which combine group name and filename to file id,
|
||||||
|
add anothor client test program: fdfs_test1.c to use file id
|
||||||
|
* client download file support callback function
|
||||||
|
* add protocol cmd TRACKER_PROTO_CMD_SERVICE_QUERY_UPDATE,
|
||||||
|
and client API add tracker_query_storage_update
|
||||||
|
* add protocol cmd TRACKER_PROTO_CMD_STORAGE_SYNC_REPORT to report last
|
||||||
|
synced timestamp as dest server
|
||||||
|
* fix sync old data files to new server bug
|
||||||
|
* fcntl change to pthread_mutex_lock
|
||||||
|
|
||||||
|
Version 1.11 2008-10-04
|
||||||
|
* kill report and sync threads when recv terminate signal
|
||||||
|
* add item "store_server" in tracker.conf, by default use the first
|
||||||
|
storage server to store uploaded files
|
||||||
|
* ini_file_reader.c changed: a conf file can include other conf files
|
||||||
|
* some adjustment:
|
||||||
|
some macro name changed
|
||||||
|
add common_define.h
|
||||||
|
remove fdfs_define.c
|
||||||
|
fdfs_os_bits.h change to _os_bits.h
|
||||||
|
|
||||||
|
Version 1.10 2008-09-20
|
||||||
|
* performance optimizing: use thread pool, create all work threads at startup
|
||||||
|
* trim function op in shared_func.c
|
||||||
|
* add Makefile template Makefile.in, delete Makefile and Makefile.freebsd
|
||||||
|
change make.sh to support all unix systems (passed in Linux and FreeBSD)
|
||||||
|
|
||||||
|
Version 1.9 2008-09-14
|
||||||
|
* security enhancement: support allow hosts which can connect to the server
|
||||||
|
* server can be run by the specified group and user, set by the config file
|
||||||
|
* change make.sh and add file common/fdfs_os_bits.h,
|
||||||
|
remove the warning info of printf format for int64_t param in 64 bits system
|
||||||
|
* storage_client.c changed: auto connect to storage server when not connected
|
||||||
|
* change some macro name and function name in tracker/tracker_proto.h
|
||||||
|
|
||||||
|
Version 1.8 2008-09-07
|
||||||
|
* communication protocol changed to support large file exceed 2GB:
|
||||||
|
# all integer field is 8 bytes big-endian
|
||||||
|
# group name fixed length: FDFS_GROUP_NAME_MAX_LEN bytes
|
||||||
|
* storage stat numbers (such as total_upload_count, success_upload_count)
|
||||||
|
use int64_t (8 bytes integer)
|
||||||
|
* ini_file_reader.c add function iniGetInt64Value
|
||||||
|
* sockopt.c add function tcpsetnonblockopt
|
||||||
|
* shared_func.c add function set_nonblock
|
||||||
|
|
||||||
|
Version 1.7 2008-08-31
|
||||||
|
* performance optimizing:
|
||||||
|
# change fopen to syscall open
|
||||||
|
# increase the efficiency of socket functions tcpsenddata and tcprecvdata
|
||||||
|
* change the return value of socket funtions such as tcpsenddata,
|
||||||
|
tcprecvdata and connectserverbyip
|
||||||
|
old return value: result=1 for success, result != 1 fail
|
||||||
|
new return value: result=0 for success, result != 0 fail, return the error code
|
||||||
|
* log function enhancement:
|
||||||
|
# support log level
|
||||||
|
# parameter "log_level" added to server config file
|
||||||
|
# keep the log file opened to increase performance
|
||||||
|
* fix log format and parameter mismatched bug (check by printf)
|
||||||
|
* log CRIT message to log file when program exit unexpectedly
|
||||||
|
* Makefile add compile flag -D_FILE_OFFSET_BITS=64 to support large files
|
||||||
|
* change the type of file_size and file_offset to off_t
|
||||||
|
* change signal to sigaction
|
||||||
|
* fix client Makefile to compile library correctly
|
||||||
|
* restart.sh modified: use external command "expr" to replace shell command "let"
|
||||||
|
|
||||||
|
Version 1.6 2008-08-24
|
||||||
|
* add restart daemon shell script: restart.sh
|
||||||
|
* use setrlimit to increase max open files if necessary
|
||||||
|
* security enhancement: the format of data filename must be: HH/HH/filename,
|
||||||
|
eg. B9/F4/SLI2NAAMRPR9r8.d
|
||||||
|
* fix bug: errno is not correct where the downloaded file does not exist,
|
||||||
|
communication is broken when the download file is a directory
|
||||||
|
|
||||||
|
Version 1.5 2008-08-17
|
||||||
|
* add client function storage_download_file_to_file
|
||||||
|
* use pthread_attr_setstacksize to increase thread stack size to 1 MB
|
||||||
|
* use sendfile syscall to send file in Linux and FreeBSD
|
||||||
|
* fix bug: add O_TRUNC flag when open file to write
|
||||||
|
* remove warning info compiled by gcc 4.2
|
||||||
|
* fcntl set lock.l_len to 0
|
||||||
|
|
||||||
|
Version 1.4 2008-08-10
|
||||||
|
* storage server recv file method change
|
||||||
|
old method: recv the whole file content/buff before write to file
|
||||||
|
new method: write to file once recv a certain bytes file buff, eg. 128KB buff size
|
||||||
|
* storage client and storage server send file method change
|
||||||
|
old method: get the whole file content/buff, then send to storage server
|
||||||
|
new method: send file to storage server more times. get a certain bytes file buff, then send to storage server
|
||||||
|
* upload file package remove the one pad byte field
|
||||||
|
* remove storage status FDFS_STORAGE_STATUS_DEACTIVE and add FDFS_STORAGE_STATUS_DELETED
|
||||||
|
|
||||||
|
Version 1.3 2008-08-03
|
||||||
|
* fix bug: when meta data is empty, get meta data return error
|
||||||
|
* support java client
|
||||||
|
# memset response header to 0
|
||||||
|
# add group_name to upload file response package
|
||||||
|
|
||||||
|
Version 1.2 2008-07-27
|
||||||
|
* add client function storage_set_metadata to support setting metadata(overwrite or merge)
|
||||||
|
|
||||||
|
Version 1.1 2008-07-20
|
||||||
|
* implement storage disk report
|
||||||
|
* storing load balance between storage groups(volumes) when set store_lookup to 2
|
||||||
|
|
||||||
|
Version 1.0 2008-07-12
|
||||||
|
* first version
|
||||||
|
|
||||||
|
|
@ -0,0 +1,188 @@
|
||||||
|
Copy right 2009 Happy Fish / YuQing
|
||||||
|
|
||||||
|
FastDFS may be copied only under the terms of the GNU General
|
||||||
|
Public License V3, which may be found in the FastDFS source kit.
|
||||||
|
Please visit the FastDFS Home Page for more detail.
|
||||||
|
English language: http://english.csource.org/
|
||||||
|
Chinese language: http://www.csource.org/
|
||||||
|
|
||||||
|
#step 1. download FastDFS source package and unpack it,
|
||||||
|
tar xzf FastDFS_v5.x.tar.gz
|
||||||
|
#for example:
|
||||||
|
tar xzf FastDFS_v5.01.tar.gz
|
||||||
|
|
||||||
|
#step 2. enter the FastDFS dir
|
||||||
|
cd FastDFS
|
||||||
|
|
||||||
|
#step 3. execute:
|
||||||
|
./make.sh
|
||||||
|
|
||||||
|
#step 4. make install
|
||||||
|
./make.sh install
|
||||||
|
|
||||||
|
#step 5. edit/modify the config file of tracker and storage
|
||||||
|
|
||||||
|
#step 6. run server programs
|
||||||
|
#start the tracker server:
|
||||||
|
/usr/local/bin/fdfs_trackerd /etc/fdfs/tracker.conf restart
|
||||||
|
#in Linux, you can start fdfs_trackerd as a service:
|
||||||
|
/sbin/service fdfs_trackerd start
|
||||||
|
|
||||||
|
#start the storage server:
|
||||||
|
/usr/local/bin/fdfs_storaged /etc/fdfs/storage.conf restart
|
||||||
|
#in Linux, you can start fdfs_storaged as a service:
|
||||||
|
/sbin/service fdfs_storaged start
|
||||||
|
|
||||||
|
#step 7. run test program
|
||||||
|
#run the client test program:
|
||||||
|
/usr/local/bin/fdfs_test <client_conf_filename> <operation>
|
||||||
|
/usr/local/bin/fdfs_test1 <client_conf_filename> <operation>
|
||||||
|
#for example, upload a file:
|
||||||
|
/usr/local/bin/fdfs_test conf/client.conf upload /usr/include/stdlib.h
|
||||||
|
|
||||||
|
#step 8. run monitor program
|
||||||
|
#run the monitor program:
|
||||||
|
/usr/local/bin/fdfs_monitor <client_conf_filename>
|
||||||
|
|
||||||
|
|
||||||
|
tracker server config file sample please see conf/tracker.conf
|
||||||
|
|
||||||
|
storage server config file sample please see conf/storage.conf
|
||||||
|
|
||||||
|
client config file sample please see conf/client.conf
|
||||||
|
|
||||||
|
|
||||||
|
Item detail
|
||||||
|
1. server common items
|
||||||
|
---------------------------------------------------
|
||||||
|
| item name | type | default | Must |
|
||||||
|
---------------------------------------------------
|
||||||
|
| base_path | string | | Y |
|
||||||
|
---------------------------------------------------
|
||||||
|
| disabled | boolean| false | N |
|
||||||
|
---------------------------------------------------
|
||||||
|
| bind_addr | string | | N |
|
||||||
|
---------------------------------------------------
|
||||||
|
| network_timeout | int | 30(s) | N |
|
||||||
|
---------------------------------------------------
|
||||||
|
| max_connections | int | 256 | N |
|
||||||
|
---------------------------------------------------
|
||||||
|
| log_level | string | info | N |
|
||||||
|
---------------------------------------------------
|
||||||
|
| run_by_group | string | | N |
|
||||||
|
---------------------------------------------------
|
||||||
|
| run_by_user | string | | N |
|
||||||
|
---------------------------------------------------
|
||||||
|
| allow_hosts | string | * | N |
|
||||||
|
---------------------------------------------------
|
||||||
|
| sync_log_buff_interval| int | 10(s) | N |
|
||||||
|
---------------------------------------------------
|
||||||
|
| thread_stack_size | string | 1M | N |
|
||||||
|
---------------------------------------------------
|
||||||
|
memo:
|
||||||
|
* base_path is the base path of sub dirs:
|
||||||
|
data and logs. base_path must exist and it's sub dirs will
|
||||||
|
be automatically created if not exist.
|
||||||
|
$base_path/data: store data files
|
||||||
|
$base_path/logs: store log files
|
||||||
|
* log_level is the standard log level as syslog, case insensitive
|
||||||
|
# emerg: for emergency
|
||||||
|
# alert
|
||||||
|
# crit: for critical
|
||||||
|
# error
|
||||||
|
# warn: for warning
|
||||||
|
# notice
|
||||||
|
# info
|
||||||
|
# debug
|
||||||
|
* allow_hosts can ocur more than once, host can be hostname or ip address,
|
||||||
|
"*" means match all ip addresses, can use range like this: 10.0.1.[1-15,20]
|
||||||
|
or host[01-08,20-25].domain.com, for example:
|
||||||
|
allow_hosts=10.0.1.[1-15,20]
|
||||||
|
allow_hosts=host[01-08,20-25].domain.com
|
||||||
|
|
||||||
|
2. tracker server items
|
||||||
|
---------------------------------------------------
|
||||||
|
| item name | type | default | Must |
|
||||||
|
---------------------------------------------------
|
||||||
|
| port | int | 22000 | N |
|
||||||
|
---------------------------------------------------
|
||||||
|
| store_lookup | int | 0 | N |
|
||||||
|
---------------------------------------------------
|
||||||
|
| store_group | string | | N |
|
||||||
|
---------------------------------------------------
|
||||||
|
| store_server | int | 0 | N |
|
||||||
|
---------------------------------------------------
|
||||||
|
| store_path | int | 0 | N |
|
||||||
|
---------------------------------------------------
|
||||||
|
| download_server | int | 0 | N |
|
||||||
|
---------------------------------------------------
|
||||||
|
| reserved_storage_space| string | 1GB | N |
|
||||||
|
---------------------------------------------------
|
||||||
|
|
||||||
|
memo:
|
||||||
|
* the value of store_lookup is:
|
||||||
|
0: round robin (default)
|
||||||
|
1: specify group
|
||||||
|
2: load balance (supported since V1.1)
|
||||||
|
* store_group is the name of group to store files.
|
||||||
|
when store_lookup set to 1(specify group),
|
||||||
|
store_group must be set to a specified group name.
|
||||||
|
* reserved_storage_space is the reserved storage space for system
|
||||||
|
or other applications. if the free(available) space of any stoarge
|
||||||
|
server in a group <= reserved_storage_space, no file can be uploaded
|
||||||
|
to this group (since V1.1)
|
||||||
|
bytes unit can be one of follows:
|
||||||
|
# G or g for gigabyte(GB)
|
||||||
|
# M or m for megabyte(MB)
|
||||||
|
# K or k for kilobyte(KB)
|
||||||
|
# no unit for byte(B)
|
||||||
|
|
||||||
|
3. storage server items
|
||||||
|
-------------------------------------------------
|
||||||
|
| item name | type | default | Must |
|
||||||
|
-------------------------------------------------
|
||||||
|
| group_name | string | | Y |
|
||||||
|
-------------------------------------------------
|
||||||
|
| tracker_server | string | | Y |
|
||||||
|
-------------------------------------------------
|
||||||
|
| port | int | 23000 | N |
|
||||||
|
-------------------------------------------------
|
||||||
|
| heart_beat_interval | int | 30(s) | N |
|
||||||
|
-------------------------------------------------
|
||||||
|
| stat_report_interval| int | 300(s) | N |
|
||||||
|
-------------------------------------------------
|
||||||
|
| sync_wait_msec | int | 100(ms) | N |
|
||||||
|
-------------------------------------------------
|
||||||
|
| sync_interval | int | 0(ms) | N |
|
||||||
|
-------------------------------------------------
|
||||||
|
| sync_start_time | string | 00:00 | N |
|
||||||
|
-------------------------------------------------
|
||||||
|
| sync_end_time | string | 23:59 | N |
|
||||||
|
-------------------------------------------------
|
||||||
|
| store_path_count | int | 1 | N |
|
||||||
|
-------------------------------------------------
|
||||||
|
| store_path0 | string |base_path| N |
|
||||||
|
-------------------------------------------------
|
||||||
|
| store_path# | string | | N |
|
||||||
|
-------------------------------------------------
|
||||||
|
|subdir_count_per_path| int | 256 | N |
|
||||||
|
-------------------------------------------------
|
||||||
|
|check_file_duplicate | boolean| 0 | N |
|
||||||
|
-------------------------------------------------
|
||||||
|
| key_namespace | string | | N |
|
||||||
|
-------------------------------------------------
|
||||||
|
| keep_alive | boolean| 0 | N |
|
||||||
|
-------------------------------------------------
|
||||||
|
| sync_binlog_buff_interval| int | 60s | N |
|
||||||
|
-------------------------------------------------
|
||||||
|
|
||||||
|
memo:
|
||||||
|
* tracker_server can ocur more than once, and tracker_server format is
|
||||||
|
"host:port", host can be hostname or ip address.
|
||||||
|
* store_path#, # for digital, based 0
|
||||||
|
* check_file_duplicate: when set to true, must work with FastDHT server,
|
||||||
|
more detail please see INSTALL of FastDHT. FastDHT download page:
|
||||||
|
http://code.google.com/p/fastdht/downloads/list
|
||||||
|
* key_namespace: FastDHT key namespace, can't be empty when
|
||||||
|
check_file_duplicate is true. the key namespace should short as possible
|
||||||
|
|
||||||
46
README.md
46
README.md
|
|
@ -1,4 +1,44 @@
|
||||||
fastdfs
|
Copyright (C) 2008 Happy Fish / YuQing
|
||||||
=======
|
|
||||||
|
|
||||||
FastDFS is an open source high performance distributed file system (DFS). It's major functions include: file storing, file syncing and file accessing, and design for high capacity and load balance.
|
FastDFS may be copied only under the terms of the GNU General
|
||||||
|
Public License V3, which may be found in the FastDFS source kit.
|
||||||
|
Please visit the FastDFS Home Page for more detail.
|
||||||
|
English language: http://english.csource.org/
|
||||||
|
Chinese language: http://www.csource.org/
|
||||||
|
|
||||||
|
|
||||||
|
FastDFS is an open source high performance distributed file system. It's major
|
||||||
|
functions include: file storing, file syncing and file accessing (file uploading
|
||||||
|
and file downloading), and it can resolve the high capacity and load balancing
|
||||||
|
problem. FastDFS should meet the requirement of the website whose service based
|
||||||
|
on files such as photo sharing site and video sharing site.
|
||||||
|
|
||||||
|
FastDFS has two roles: tracker and storage. The tracker takes charge of
|
||||||
|
scheduling and load balancing for file access. The storage store files and it's
|
||||||
|
function is file management including: file storing, file syncing, providing file
|
||||||
|
access interface. It also manage the meta data which are attributes representing
|
||||||
|
as key value pair of the file. For example: width=1024, the key is "width" and
|
||||||
|
the value is "1024".
|
||||||
|
|
||||||
|
The tracker and storage contain one or more servers. The servers in the tracker
|
||||||
|
or storage cluster can be added to or removed from the cluster by any time without
|
||||||
|
affecting the online services. The servers in the tracker cluster are peer to peer.
|
||||||
|
|
||||||
|
The storarge servers organizing by the file volume/group to obtain high capacity.
|
||||||
|
The storage system contains one or more volumes whose files are independent among
|
||||||
|
these volumes. The capacity of the whole storage system equals to the sum of all
|
||||||
|
volumes' capacity. A file volume contains one or more storage servers whose files
|
||||||
|
are same among these servers. The servers in a file volume backup each other,
|
||||||
|
and all these servers are load balancing. When adding a storage server to a
|
||||||
|
volume, files already existing in this volume are replicated to this new server
|
||||||
|
automatically, and when this replication done, system will switch this server
|
||||||
|
online to providing storage services.
|
||||||
|
|
||||||
|
When the whole storage capacity is insufficiency, you can add one or more
|
||||||
|
volumes to expand the storage capacity. To do this, you need to add one or
|
||||||
|
more storage servers.
|
||||||
|
|
||||||
|
The identification of a file is composed of two parts: the volume name and
|
||||||
|
the file name.
|
||||||
|
|
||||||
|
Client test code use client library please refer to the directory: client/test.
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,115 @@
|
||||||
|
.SUFFIXES: .c .o .lo
|
||||||
|
|
||||||
|
COMPILE = $(CC) $(CFLAGS)
|
||||||
|
ENABLE_STATIC_LIB = $(ENABLE_STATIC_LIB)
|
||||||
|
ENABLE_SHARED_LIB = $(ENABLE_SHARED_LIB)
|
||||||
|
INC_PATH = -I../common -I../tracker -I/usr/local/include
|
||||||
|
LIB_PATH = $(LIBS)
|
||||||
|
TARGET_PATH = $(TARGET_PREFIX)/bin
|
||||||
|
TARGET_LIB = $(TARGET_PREFIX)/lib
|
||||||
|
TARGET_INC = $(TARGET_PREFIX)/include
|
||||||
|
CONFIG_PATH = $(TARGET_CONF_PATH)
|
||||||
|
|
||||||
|
FAST_STATIC_OBJS = ../common/hash.o ../common/chain.o \
|
||||||
|
../common/shared_func.o ../common/ini_file_reader.o \
|
||||||
|
../common/logger.o ../common/sockopt.o \
|
||||||
|
../common/base64.o ../common/sched_thread.o \
|
||||||
|
../common/http_func.o ../common/md5.o \
|
||||||
|
../common/pthread_func.o ../common/local_ip_func.o \
|
||||||
|
../common/avl_tree.o ../common/connection_pool.o
|
||||||
|
|
||||||
|
FDFS_STATIC_OBJS = ../common/fdfs_global.o ../common/fdfs_http_shared.o \
|
||||||
|
../common/mime_file_parser.o ../tracker/tracker_proto.o \
|
||||||
|
../tracker/fdfs_shared_func.o \
|
||||||
|
../storage/trunk_mgr/trunk_shared.o \
|
||||||
|
tracker_client.o client_func.o \
|
||||||
|
client_global.o storage_client.o
|
||||||
|
|
||||||
|
STATIC_OBJS = $(FAST_STATIC_OBJS) $(FDFS_STATIC_OBJS)
|
||||||
|
|
||||||
|
FAST_SHARED_OBJS = ../common/hash.lo ../common/chain.lo \
|
||||||
|
../common/shared_func.lo ../common/ini_file_reader.lo \
|
||||||
|
../common/logger.lo ../common/sockopt.lo \
|
||||||
|
../common/base64.lo ../common/sched_thread.lo \
|
||||||
|
../common/http_func.lo ../common/md5.lo \
|
||||||
|
../common/pthread_func.lo ../common/local_ip_func.lo \
|
||||||
|
../common/avl_tree.lo ../common/connection_pool.lo
|
||||||
|
|
||||||
|
FDFS_SHARED_OBJS = ../common/fdfs_global.lo ../common/fdfs_http_shared.lo \
|
||||||
|
../common/mime_file_parser.lo ../tracker/tracker_proto.lo \
|
||||||
|
../tracker/fdfs_shared_func.lo \
|
||||||
|
../storage/trunk_mgr/trunk_shared.lo \
|
||||||
|
tracker_client.lo client_func.lo \
|
||||||
|
client_global.lo storage_client.lo
|
||||||
|
|
||||||
|
FAST_HEADER_FILES = ../common/common_define.h ../common/hash.h \
|
||||||
|
../common/chain.h ../common/logger.h \
|
||||||
|
../common/base64.h ../common/shared_func.h \
|
||||||
|
../common/pthread_func.h ../common/ini_file_reader.h \
|
||||||
|
../common/sockopt.h ../common/sched_thread.h \
|
||||||
|
../common/http_func.h ../common/md5.h ../common/_os_bits.h \
|
||||||
|
../common/local_ip_func.h ../common/avl_tree.h \
|
||||||
|
../common/connection_pool.h
|
||||||
|
|
||||||
|
FDFS_HEADER_FILES = ../common/fdfs_define.h ../common/fdfs_global.h \
|
||||||
|
../common/mime_file_parser.h ../common/fdfs_http_shared.h \
|
||||||
|
../tracker/tracker_types.h ../tracker/tracker_proto.h \
|
||||||
|
../tracker/fdfs_shared_func.h \
|
||||||
|
../storage/trunk_mgr/trunk_shared.h \
|
||||||
|
tracker_client.h storage_client.h storage_client1.h \
|
||||||
|
client_func.h client_global.h fdfs_client.h
|
||||||
|
|
||||||
|
ALL_OBJS = $(STATIC_OBJS) $(FAST_SHARED_OBJS) $(FDFS_SHARED_OBJS)
|
||||||
|
|
||||||
|
ALL_PRGS = fdfs_monitor fdfs_test fdfs_test1 fdfs_crc32 fdfs_upload_file \
|
||||||
|
fdfs_download_file fdfs_delete_file fdfs_file_info \
|
||||||
|
fdfs_appender_test fdfs_appender_test1 fdfs_append_file \
|
||||||
|
fdfs_upload_appender
|
||||||
|
|
||||||
|
STATIC_LIBS = libfastcommon.a libfdfsclient.a
|
||||||
|
|
||||||
|
SHARED_LIBS = libfastcommon.so.1 libfdfsclient.so.1
|
||||||
|
|
||||||
|
ALL_LIBS = $(STATIC_LIBS) $(SHARED_LIBS)
|
||||||
|
|
||||||
|
all: $(ALL_OBJS) $(ALL_PRGS) $(ALL_LIBS)
|
||||||
|
libfastcommon.so.1:
|
||||||
|
$(COMPILE) -o $@ $< -shared $(FAST_SHARED_OBJS) $(LIB_PATH)
|
||||||
|
ln -fs libfastcommon.so.1 libfastcommon.so
|
||||||
|
libfdfsclient.so.1:
|
||||||
|
$(COMPILE) -o $@ $< -shared $(FDFS_SHARED_OBJS) $(LIB_PATH) -L. -lfastcommon
|
||||||
|
ln -fs libfdfsclient.so.1 libfdfsclient.so
|
||||||
|
libfastcommon.a:
|
||||||
|
ar cru $@ $< $(FAST_STATIC_OBJS)
|
||||||
|
libfdfsclient.a:
|
||||||
|
ar cru $@ $< $(FDFS_STATIC_OBJS)
|
||||||
|
.o:
|
||||||
|
$(COMPILE) -o $@ $< $(STATIC_OBJS) $(LIB_PATH) $(INC_PATH)
|
||||||
|
.c:
|
||||||
|
$(COMPILE) -o $@ $< $(STATIC_OBJS) $(LIB_PATH) $(INC_PATH)
|
||||||
|
.c.o:
|
||||||
|
$(COMPILE) -c -o $@ $< $(INC_PATH)
|
||||||
|
.c.lo:
|
||||||
|
$(COMPILE) -c -fPIC -o $@ $< $(INC_PATH)
|
||||||
|
install:
|
||||||
|
mkdir -p $(TARGET_PATH)
|
||||||
|
mkdir -p $(CONFIG_PATH)
|
||||||
|
mkdir -p $(TARGET_LIB)
|
||||||
|
cp -f $(ALL_PRGS) $(TARGET_PATH)
|
||||||
|
if [ $(ENABLE_STATIC_LIB) -eq 1 ]; then cp -f $(STATIC_LIBS) $(TARGET_LIB); fi
|
||||||
|
if [ $(ENABLE_SHARED_LIB) -eq 1 ]; then cp -f $(SHARED_LIBS) $(TARGET_LIB); fi
|
||||||
|
if [ $(ENABLE_SHARED_LIB) -eq 1 ]; then ln -fs $(TARGET_LIB)/libfastcommon.so.1 $(TARGET_LIB)/libfastcommon.so; fi
|
||||||
|
if [ $(ENABLE_SHARED_LIB) -eq 1 ]; then ln -fs $(TARGET_LIB)/libfdfsclient.so.1 $(TARGET_LIB)/libfdfsclient.so; fi
|
||||||
|
|
||||||
|
if [ ! -f $(CONFIG_PATH)/client.conf ]; then cp -f ../conf/client.conf ../conf/http.conf $(CONFIG_PATH); fi
|
||||||
|
|
||||||
|
mkdir -p $(TARGET_INC)
|
||||||
|
mkdir -p $(TARGET_INC)/fastcommon
|
||||||
|
mkdir -p $(TARGET_INC)/fastdfs
|
||||||
|
cp -f $(FAST_HEADER_FILES) $(TARGET_INC)/fastcommon
|
||||||
|
cp -f $(FDFS_HEADER_FILES) $(TARGET_INC)/fastdfs
|
||||||
|
|
||||||
|
if [ $(ENABLE_SHARED_LIB) -eq 1 -a $(TARGET_LIB) = "/usr/local/lib" ]; then sh ./fdfs_link_library.sh; fi
|
||||||
|
clean:
|
||||||
|
rm -f $(ALL_OBJS) $(ALL_PRGS) $(ALL_LIBS) libfastcommon.so libfdfsclient.so
|
||||||
|
|
||||||
|
|
@ -0,0 +1,537 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2008 Happy Fish / YuQing
|
||||||
|
*
|
||||||
|
* FastDFS may be copied only under the terms of the GNU General
|
||||||
|
* Public License V3, which may be found in the FastDFS source kit.
|
||||||
|
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||||
|
**/
|
||||||
|
|
||||||
|
//client_func.c
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include "fdfs_define.h"
|
||||||
|
#include "logger.h"
|
||||||
|
#include "fdfs_global.h"
|
||||||
|
#include "base64.h"
|
||||||
|
#include "sockopt.h"
|
||||||
|
#include "shared_func.h"
|
||||||
|
#include "ini_file_reader.h"
|
||||||
|
#include "connection_pool.h"
|
||||||
|
#include "tracker_types.h"
|
||||||
|
#include "tracker_proto.h"
|
||||||
|
#include "client_global.h"
|
||||||
|
#include "client_func.h"
|
||||||
|
|
||||||
|
static int storage_cmp_by_ip_and_port(const void *p1, const void *p2)
|
||||||
|
{
|
||||||
|
int res;
|
||||||
|
|
||||||
|
res = strcmp(((ConnectionInfo *)p1)->ip_addr, \
|
||||||
|
((ConnectionInfo *)p2)->ip_addr);
|
||||||
|
if (res != 0)
|
||||||
|
{
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ((ConnectionInfo *)p1)->port - \
|
||||||
|
((ConnectionInfo *)p2)->port;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void insert_into_sorted_servers(TrackerServerGroup *pTrackerGroup, \
|
||||||
|
ConnectionInfo *pInsertedServer)
|
||||||
|
{
|
||||||
|
ConnectionInfo *pDestServer;
|
||||||
|
for (pDestServer=pTrackerGroup->servers+pTrackerGroup->server_count; \
|
||||||
|
pDestServer>pTrackerGroup->servers; pDestServer--)
|
||||||
|
{
|
||||||
|
if (storage_cmp_by_ip_and_port(pInsertedServer, \
|
||||||
|
pDestServer-1) > 0)
|
||||||
|
{
|
||||||
|
memcpy(pDestServer, pInsertedServer, \
|
||||||
|
sizeof(ConnectionInfo));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(pDestServer, pDestServer-1, sizeof(ConnectionInfo));
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(pDestServer, pInsertedServer, sizeof(ConnectionInfo));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int copy_tracker_servers(TrackerServerGroup *pTrackerGroup, \
|
||||||
|
const char *filename, char **ppTrackerServers)
|
||||||
|
{
|
||||||
|
char **ppSrc;
|
||||||
|
char **ppEnd;
|
||||||
|
ConnectionInfo destServer;
|
||||||
|
char *pSeperator;
|
||||||
|
char szHost[128];
|
||||||
|
int nHostLen;
|
||||||
|
|
||||||
|
memset(&destServer, 0, sizeof(ConnectionInfo));
|
||||||
|
destServer.sock = -1;
|
||||||
|
|
||||||
|
ppEnd = ppTrackerServers + pTrackerGroup->server_count;
|
||||||
|
|
||||||
|
pTrackerGroup->server_count = 0;
|
||||||
|
for (ppSrc=ppTrackerServers; ppSrc<ppEnd; ppSrc++)
|
||||||
|
{
|
||||||
|
if ((pSeperator=strchr(*ppSrc, ':')) == NULL)
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"conf file \"%s\", " \
|
||||||
|
"tracker_server \"%s\" is invalid, " \
|
||||||
|
"correct format is host:port", \
|
||||||
|
__LINE__, filename, *ppSrc);
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
nHostLen = pSeperator - (*ppSrc);
|
||||||
|
if (nHostLen >= sizeof(szHost))
|
||||||
|
{
|
||||||
|
nHostLen = sizeof(szHost) - 1;
|
||||||
|
}
|
||||||
|
memcpy(szHost, *ppSrc, nHostLen);
|
||||||
|
szHost[nHostLen] = '\0';
|
||||||
|
|
||||||
|
if (getIpaddrByName(szHost, destServer.ip_addr, \
|
||||||
|
sizeof(destServer.ip_addr)) == INADDR_NONE)
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"conf file \"%s\", " \
|
||||||
|
"host \"%s\" is invalid", \
|
||||||
|
__LINE__, filename, szHost);
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
destServer.port = atoi(pSeperator+1);
|
||||||
|
if (destServer.port <= 0)
|
||||||
|
{
|
||||||
|
destServer.port = FDFS_TRACKER_SERVER_DEF_PORT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bsearch(&destServer, pTrackerGroup->servers, \
|
||||||
|
pTrackerGroup->server_count, \
|
||||||
|
sizeof(ConnectionInfo), \
|
||||||
|
storage_cmp_by_ip_and_port) == NULL)
|
||||||
|
{
|
||||||
|
insert_into_sorted_servers(pTrackerGroup, &destServer);
|
||||||
|
pTrackerGroup->server_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
{
|
||||||
|
ConnectionInfo *pServer;
|
||||||
|
for (pServer=pTrackerGroup->servers; pServer<pTrackerGroup->servers+ \
|
||||||
|
pTrackerGroup->server_count; pServer++)
|
||||||
|
{
|
||||||
|
//printf("server=%s:%d\n", \
|
||||||
|
pServer->ip_addr, pServer->port);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fdfs_load_tracker_group_ex(TrackerServerGroup *pTrackerGroup, \
|
||||||
|
const char *conf_filename, IniContext *pIniContext)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
char *ppTrackerServers[FDFS_MAX_TRACKERS];
|
||||||
|
|
||||||
|
if ((pTrackerGroup->server_count=iniGetValues(NULL, "tracker_server", \
|
||||||
|
pIniContext, ppTrackerServers, FDFS_MAX_TRACKERS)) <= 0)
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"conf file \"%s\", " \
|
||||||
|
"get item \"tracker_server\" fail", \
|
||||||
|
__LINE__, conf_filename);
|
||||||
|
return ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
pTrackerGroup->servers = (ConnectionInfo *)malloc( \
|
||||||
|
sizeof(ConnectionInfo) * pTrackerGroup->server_count);
|
||||||
|
if (pTrackerGroup->servers == NULL)
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"malloc %d bytes fail", __LINE__, \
|
||||||
|
(int)sizeof(ConnectionInfo) * \
|
||||||
|
pTrackerGroup->server_count);
|
||||||
|
pTrackerGroup->server_count = 0;
|
||||||
|
return errno != 0 ? errno : ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(pTrackerGroup->servers, 0, \
|
||||||
|
sizeof(ConnectionInfo) * pTrackerGroup->server_count);
|
||||||
|
if ((result=copy_tracker_servers(pTrackerGroup, conf_filename, \
|
||||||
|
ppTrackerServers)) != 0)
|
||||||
|
{
|
||||||
|
pTrackerGroup->server_count = 0;
|
||||||
|
free(pTrackerGroup->servers);
|
||||||
|
pTrackerGroup->servers = NULL;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fdfs_load_tracker_group(TrackerServerGroup *pTrackerGroup, \
|
||||||
|
const char *conf_filename)
|
||||||
|
{
|
||||||
|
IniContext iniContext;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
if ((result=iniLoadFromFile(conf_filename, &iniContext)) != 0)
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"load conf file \"%s\" fail, ret code: %d", \
|
||||||
|
__LINE__, conf_filename, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = fdfs_load_tracker_group_ex(pTrackerGroup, conf_filename, \
|
||||||
|
&iniContext);
|
||||||
|
iniFreeContext(&iniContext);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fdfs_get_params_from_tracker(bool *use_storage_id)
|
||||||
|
{
|
||||||
|
IniContext iniContext;
|
||||||
|
int result;
|
||||||
|
bool continue_flag;
|
||||||
|
|
||||||
|
continue_flag = false;
|
||||||
|
if ((result=fdfs_get_ini_context_from_tracker(&g_tracker_group, \
|
||||||
|
&iniContext, &continue_flag, false, NULL)) != 0)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
*use_storage_id = iniGetBoolValue(NULL, "use_storage_id", \
|
||||||
|
&iniContext, false);
|
||||||
|
iniFreeContext(&iniContext);
|
||||||
|
|
||||||
|
if (*use_storage_id)
|
||||||
|
{
|
||||||
|
result = fdfs_get_storage_ids_from_tracker_group( \
|
||||||
|
&g_tracker_group);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fdfs_client_do_init_ex(TrackerServerGroup *pTrackerGroup, \
|
||||||
|
const char *conf_filename, IniContext *iniContext)
|
||||||
|
{
|
||||||
|
char *pBasePath;
|
||||||
|
int result;
|
||||||
|
bool use_storage_id;
|
||||||
|
bool load_fdfs_parameters_from_tracker;
|
||||||
|
|
||||||
|
pBasePath = iniGetStrValue(NULL, "base_path", iniContext);
|
||||||
|
if (pBasePath == NULL)
|
||||||
|
{
|
||||||
|
strcpy(g_fdfs_base_path, "/tmp");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
snprintf(g_fdfs_base_path, sizeof(g_fdfs_base_path),
|
||||||
|
"%s", pBasePath);
|
||||||
|
chopPath(g_fdfs_base_path);
|
||||||
|
if (!fileExists(g_fdfs_base_path))
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"\"%s\" can't be accessed, error info: %s", \
|
||||||
|
__LINE__, g_fdfs_base_path, STRERROR(errno));
|
||||||
|
return errno != 0 ? errno : ENOENT;
|
||||||
|
}
|
||||||
|
if (!isDir(g_fdfs_base_path))
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"\"%s\" is not a directory!", \
|
||||||
|
__LINE__, g_fdfs_base_path);
|
||||||
|
return ENOTDIR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
g_fdfs_connect_timeout = iniGetIntValue(NULL, "connect_timeout", \
|
||||||
|
iniContext, DEFAULT_CONNECT_TIMEOUT);
|
||||||
|
if (g_fdfs_connect_timeout <= 0)
|
||||||
|
{
|
||||||
|
g_fdfs_connect_timeout = DEFAULT_CONNECT_TIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_fdfs_network_timeout = iniGetIntValue(NULL, "network_timeout", \
|
||||||
|
iniContext, DEFAULT_NETWORK_TIMEOUT);
|
||||||
|
if (g_fdfs_network_timeout <= 0)
|
||||||
|
{
|
||||||
|
g_fdfs_network_timeout = DEFAULT_NETWORK_TIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((result=fdfs_load_tracker_group_ex(pTrackerGroup, \
|
||||||
|
conf_filename, iniContext)) != 0)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_anti_steal_token = iniGetBoolValue(NULL, \
|
||||||
|
"http.anti_steal.check_token", \
|
||||||
|
iniContext, false);
|
||||||
|
if (g_anti_steal_token)
|
||||||
|
{
|
||||||
|
char *anti_steal_secret_key;
|
||||||
|
|
||||||
|
anti_steal_secret_key = iniGetStrValue(NULL, \
|
||||||
|
"http.anti_steal.secret_key", \
|
||||||
|
iniContext);
|
||||||
|
if (anti_steal_secret_key == NULL || \
|
||||||
|
*anti_steal_secret_key == '\0')
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"param \"http.anti_steal.secret_key\""\
|
||||||
|
" not exist or is empty", __LINE__);
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer_strcpy(&g_anti_steal_secret_key, anti_steal_secret_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_tracker_server_http_port = iniGetIntValue(NULL, \
|
||||||
|
"http.tracker_server_port", \
|
||||||
|
iniContext, 80);
|
||||||
|
if (g_tracker_server_http_port <= 0)
|
||||||
|
{
|
||||||
|
g_tracker_server_http_port = 80;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((result=fdfs_connection_pool_init(conf_filename, iniContext)) != 0)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
load_fdfs_parameters_from_tracker = iniGetBoolValue(NULL, \
|
||||||
|
"load_fdfs_parameters_from_tracker", \
|
||||||
|
iniContext, false);
|
||||||
|
if (load_fdfs_parameters_from_tracker)
|
||||||
|
{
|
||||||
|
fdfs_get_params_from_tracker(&use_storage_id);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
use_storage_id = iniGetBoolValue(NULL, "use_storage_id", \
|
||||||
|
iniContext, false);
|
||||||
|
if (use_storage_id)
|
||||||
|
{
|
||||||
|
result = fdfs_load_storage_ids_from_file( \
|
||||||
|
conf_filename, iniContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG_FLAG
|
||||||
|
logDebug("base_path=%s, " \
|
||||||
|
"connect_timeout=%d, "\
|
||||||
|
"network_timeout=%d, "\
|
||||||
|
"tracker_server_count=%d, " \
|
||||||
|
"anti_steal_token=%d, " \
|
||||||
|
"anti_steal_secret_key length=%d, " \
|
||||||
|
"use_connection_pool=%d, " \
|
||||||
|
"g_connection_pool_max_idle_time=%ds, " \
|
||||||
|
"use_storage_id=%d, storage server id count: %d\n", \
|
||||||
|
g_fdfs_base_path, g_fdfs_connect_timeout, \
|
||||||
|
g_fdfs_network_timeout, pTrackerGroup->server_count, \
|
||||||
|
g_anti_steal_token, g_anti_steal_secret_key.length, \
|
||||||
|
g_use_connection_pool, g_connection_pool_max_idle_time, \
|
||||||
|
use_storage_id, g_storage_id_count);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fdfs_client_init_from_buffer_ex(TrackerServerGroup *pTrackerGroup, \
|
||||||
|
const char *buffer)
|
||||||
|
{
|
||||||
|
IniContext iniContext;
|
||||||
|
char *new_buff;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
new_buff = strdup(buffer);
|
||||||
|
if (new_buff == NULL)
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"strdup %d bytes fail", __LINE__, (int)strlen(buffer));
|
||||||
|
return ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = iniLoadFromBuffer(new_buff, &iniContext);
|
||||||
|
free(new_buff);
|
||||||
|
if (result != 0)
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"load parameters from buffer fail, ret code: %d", \
|
||||||
|
__LINE__, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = fdfs_client_do_init_ex(pTrackerGroup, "buffer", &iniContext);
|
||||||
|
iniFreeContext(&iniContext);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fdfs_client_init_ex(TrackerServerGroup *pTrackerGroup, \
|
||||||
|
const char *conf_filename)
|
||||||
|
{
|
||||||
|
IniContext iniContext;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
if ((result=iniLoadFromFile(conf_filename, &iniContext)) != 0)
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"load conf file \"%s\" fail, ret code: %d", \
|
||||||
|
__LINE__, conf_filename, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = fdfs_client_do_init_ex(pTrackerGroup, conf_filename, \
|
||||||
|
&iniContext);
|
||||||
|
iniFreeContext(&iniContext);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fdfs_copy_tracker_group(TrackerServerGroup *pDestTrackerGroup, \
|
||||||
|
TrackerServerGroup *pSrcTrackerGroup)
|
||||||
|
{
|
||||||
|
int bytes;
|
||||||
|
ConnectionInfo *pDestServer;
|
||||||
|
ConnectionInfo *pDestServerEnd;
|
||||||
|
|
||||||
|
bytes = sizeof(ConnectionInfo) * pSrcTrackerGroup->server_count;
|
||||||
|
pDestTrackerGroup->servers = (ConnectionInfo *)malloc(bytes);
|
||||||
|
if (pDestTrackerGroup->servers == NULL)
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"malloc %d bytes fail", __LINE__, bytes);
|
||||||
|
return errno != 0 ? errno : ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
pDestTrackerGroup->server_index = 0;
|
||||||
|
pDestTrackerGroup->server_count = pSrcTrackerGroup->server_count;
|
||||||
|
memcpy(pDestTrackerGroup->servers, pSrcTrackerGroup->servers, bytes);
|
||||||
|
|
||||||
|
pDestServerEnd = pDestTrackerGroup->servers + \
|
||||||
|
pDestTrackerGroup->server_count;
|
||||||
|
for (pDestServer=pDestTrackerGroup->servers; \
|
||||||
|
pDestServer<pDestServerEnd; pDestServer++)
|
||||||
|
{
|
||||||
|
pDestServer->sock = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool fdfs_tracker_group_equals(TrackerServerGroup *pGroup1, \
|
||||||
|
TrackerServerGroup *pGroup2)
|
||||||
|
{
|
||||||
|
ConnectionInfo *pServer1;
|
||||||
|
ConnectionInfo *pServer2;
|
||||||
|
ConnectionInfo *pEnd1;
|
||||||
|
|
||||||
|
if (pGroup1->server_count != pGroup1->server_count)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
pEnd1 = pGroup1->servers + pGroup1->server_count;
|
||||||
|
pServer1 = pGroup1->servers;
|
||||||
|
pServer2 = pGroup2->servers;
|
||||||
|
while (pServer1 < pEnd1)
|
||||||
|
{
|
||||||
|
if (!(strcmp(pServer1->ip_addr, pServer2->ip_addr) == 0 &&
|
||||||
|
pServer1->port == pServer2->port))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
pServer1++;
|
||||||
|
pServer2++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fdfs_client_destroy_ex(TrackerServerGroup *pTrackerGroup)
|
||||||
|
{
|
||||||
|
if (pTrackerGroup->servers != NULL)
|
||||||
|
{
|
||||||
|
free(pTrackerGroup->servers);
|
||||||
|
pTrackerGroup->servers = NULL;
|
||||||
|
|
||||||
|
pTrackerGroup->server_count = 0;
|
||||||
|
pTrackerGroup->server_index = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *fdfs_get_file_ext_name_ex(const char *filename,
|
||||||
|
const bool twoExtName)
|
||||||
|
{
|
||||||
|
const char *fileExtName;
|
||||||
|
const char *p;
|
||||||
|
const char *pStart;
|
||||||
|
int extNameLen;
|
||||||
|
|
||||||
|
fileExtName = strrchr(filename, '.');
|
||||||
|
if (fileExtName == NULL)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
extNameLen = strlen(fileExtName + 1);
|
||||||
|
if (extNameLen > FDFS_FILE_EXT_NAME_MAX_LEN)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strchr(fileExtName + 1, '/') != NULL) //invalid extension name
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!twoExtName)
|
||||||
|
{
|
||||||
|
return fileExtName + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pStart = fileExtName - (FDFS_FILE_EXT_NAME_MAX_LEN - extNameLen) - 1;
|
||||||
|
if (pStart < filename)
|
||||||
|
{
|
||||||
|
pStart = filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
p = fileExtName - 1; //before .
|
||||||
|
while ((p > pStart) && (*p != '.'))
|
||||||
|
{
|
||||||
|
p--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p > pStart) //found (extension name have a dot)
|
||||||
|
{
|
||||||
|
if (strchr(p + 1, '/') == NULL) //valid extension name
|
||||||
|
{
|
||||||
|
return p + 1; //skip .
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fileExtName + 1; //skip .
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,145 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2008 Happy Fish / YuQing
|
||||||
|
*
|
||||||
|
* FastDFS may be copied only under the terms of the GNU General
|
||||||
|
* Public License V3, which may be found in the FastDFS source kit.
|
||||||
|
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||||
|
**/
|
||||||
|
|
||||||
|
//client_func.h
|
||||||
|
|
||||||
|
#include "tracker_types.h"
|
||||||
|
#include "client_global.h"
|
||||||
|
#include "ini_file_reader.h"
|
||||||
|
|
||||||
|
#ifndef _CLIENT_FUNC_H_
|
||||||
|
#define _CLIENT_FUNC_H_
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
time_t create_timestamp;
|
||||||
|
int crc32;
|
||||||
|
int source_id; //source storage id
|
||||||
|
int64_t file_size;
|
||||||
|
char source_ip_addr[IP_ADDRESS_SIZE]; //source storage ip address
|
||||||
|
} FDFSFileInfo;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define fdfs_client_init(filename) \
|
||||||
|
fdfs_client_init_ex((&g_tracker_group), filename)
|
||||||
|
|
||||||
|
#define fdfs_client_init_from_buffer(buffer) \
|
||||||
|
fdfs_client_init_from_buffer_ex((&g_tracker_group), buffer)
|
||||||
|
|
||||||
|
#define fdfs_client_destroy() \
|
||||||
|
fdfs_client_destroy_ex((&g_tracker_group))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* client initial from config file
|
||||||
|
* params:
|
||||||
|
* pTrackerGroup: tracker group
|
||||||
|
* conf_filename: client config filename
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
int fdfs_client_init_ex(TrackerServerGroup *pTrackerGroup, \
|
||||||
|
const char *conf_filename);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* client initial from buffer
|
||||||
|
* params:
|
||||||
|
* pTrackerGroup: tracker group
|
||||||
|
* conf_filename: client config filename
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
int fdfs_client_init_from_buffer_ex(TrackerServerGroup *pTrackerGroup, \
|
||||||
|
const char *buffer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* load tracker server group
|
||||||
|
* params:
|
||||||
|
* pTrackerGroup: tracker group
|
||||||
|
* conf_filename: tracker server group config filename
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
int fdfs_load_tracker_group(TrackerServerGroup *pTrackerGroup, \
|
||||||
|
const char *conf_filename);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* load tracker server group
|
||||||
|
* params:
|
||||||
|
* pTrackerGroup: tracker group
|
||||||
|
* conf_filename: config filename
|
||||||
|
* items: ini file items
|
||||||
|
* nItemCount: ini file item count
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
int fdfs_load_tracker_group_ex(TrackerServerGroup *pTrackerGroup, \
|
||||||
|
const char *conf_filename, IniContext *pIniContext);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* copy tracker server group
|
||||||
|
* params:
|
||||||
|
* pDestTrackerGroup: the dest tracker group
|
||||||
|
* pSrcTrackerGroup: the source tracker group
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
int fdfs_copy_tracker_group(TrackerServerGroup *pDestTrackerGroup, \
|
||||||
|
TrackerServerGroup *pSrcTrackerGroup);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* client destroy function
|
||||||
|
* params:
|
||||||
|
* pTrackerGroup: tracker group
|
||||||
|
* return: none
|
||||||
|
**/
|
||||||
|
void fdfs_client_destroy_ex(TrackerServerGroup *pTrackerGroup);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* tracker group equals
|
||||||
|
* params:
|
||||||
|
* pGroup1: tracker group 1
|
||||||
|
* pGroup2: tracker group 2
|
||||||
|
* return: true for equals, otherwise false
|
||||||
|
**/
|
||||||
|
bool fdfs_tracker_group_equals(TrackerServerGroup *pGroup1, \
|
||||||
|
TrackerServerGroup *pGroup2);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get file ext name from filename, extension name do not include dot
|
||||||
|
* params:
|
||||||
|
* filename: the filename
|
||||||
|
* return: file ext name, NULL for no ext name
|
||||||
|
**/
|
||||||
|
#define fdfs_get_file_ext_name1(filename) \
|
||||||
|
fdfs_get_file_ext_name_ex(filename, false)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get file ext name from filename, extension name maybe include dot
|
||||||
|
* params:
|
||||||
|
* filename: the filename
|
||||||
|
* return: file ext name, NULL for no ext name
|
||||||
|
**/
|
||||||
|
#define fdfs_get_file_ext_name2(filename) \
|
||||||
|
fdfs_get_file_ext_name_ex(filename, true)
|
||||||
|
|
||||||
|
#define fdfs_get_file_ext_name(filename) \
|
||||||
|
fdfs_get_file_ext_name_ex(filename, true)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get file ext name from filename
|
||||||
|
* params:
|
||||||
|
* filename: the filename
|
||||||
|
* twoExtName: two extension name as the extension name
|
||||||
|
* return: file ext name, NULL for no ext name
|
||||||
|
**/
|
||||||
|
const char *fdfs_get_file_ext_name_ex(const char *filename,
|
||||||
|
const bool twoExtName);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2008 Happy Fish / YuQing
|
||||||
|
*
|
||||||
|
* FastDFS may be copied only under the terms of the GNU General
|
||||||
|
* Public License V3, which may be found in the FastDFS source kit.
|
||||||
|
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||||
|
**/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "client_global.h"
|
||||||
|
|
||||||
|
int g_tracker_server_http_port = 80;
|
||||||
|
TrackerServerGroup g_tracker_group = {0, 0, -1, NULL};
|
||||||
|
|
||||||
|
bool g_anti_steal_token = false;
|
||||||
|
BufferInfo g_anti_steal_secret_key = {0};
|
||||||
|
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2008 Happy Fish / YuQing
|
||||||
|
*
|
||||||
|
* FastDFS may be copied only under the terms of the GNU General
|
||||||
|
* Public License V3, which may be found in the FastDFS source kit.
|
||||||
|
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||||
|
**/
|
||||||
|
|
||||||
|
//client_global.h
|
||||||
|
|
||||||
|
#ifndef _CLIENT_GLOBAL_H
|
||||||
|
#define _CLIENT_GLOBAL_H
|
||||||
|
|
||||||
|
#include "common_define.h"
|
||||||
|
#include "tracker_types.h"
|
||||||
|
#include "fdfs_shared_func.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern int g_tracker_server_http_port;
|
||||||
|
extern TrackerServerGroup g_tracker_group;
|
||||||
|
|
||||||
|
extern bool g_anti_steal_token;
|
||||||
|
extern BufferInfo g_anti_steal_secret_key;
|
||||||
|
|
||||||
|
#define fdfs_get_tracker_leader_index(leaderIp, leaderPort) \
|
||||||
|
fdfs_get_tracker_leader_index_ex(&g_tracker_group, \
|
||||||
|
leaderIp, leaderPort)
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,66 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2008 Happy Fish / YuQing
|
||||||
|
*
|
||||||
|
* FastDFS may be copied only under the terms of the GNU General
|
||||||
|
* Public License V3, which may be found in the FastDFS source kit.
|
||||||
|
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||||
|
**/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include "fdfs_client.h"
|
||||||
|
#include "logger.h"
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
char *conf_filename;
|
||||||
|
char *local_filename;
|
||||||
|
ConnectionInfo *pTrackerServer;
|
||||||
|
int result;
|
||||||
|
char appender_file_id[128];
|
||||||
|
|
||||||
|
if (argc < 4)
|
||||||
|
{
|
||||||
|
printf("Usage: %s <config_file> <appender_file_id> " \
|
||||||
|
"<local_filename>\n", argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_init();
|
||||||
|
g_log_context.log_level = LOG_ERR;
|
||||||
|
|
||||||
|
conf_filename = argv[1];
|
||||||
|
if ((result=fdfs_client_init(conf_filename)) != 0)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
pTrackerServer = tracker_get_connection();
|
||||||
|
if (pTrackerServer == NULL)
|
||||||
|
{
|
||||||
|
fdfs_client_destroy();
|
||||||
|
return errno != 0 ? errno : ECONNREFUSED;
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(appender_file_id, sizeof(appender_file_id), "%s", argv[2]);
|
||||||
|
local_filename = argv[3];
|
||||||
|
if ((result=storage_append_by_filename1(pTrackerServer, \
|
||||||
|
NULL, local_filename, appender_file_id)) != 0)
|
||||||
|
{
|
||||||
|
printf("append file fail, " \
|
||||||
|
"error no: %d, error info: %s\n", \
|
||||||
|
result, STRERROR(result));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
tracker_disconnect_server_ex(pTrackerServer, true);
|
||||||
|
fdfs_client_destroy();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,439 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2008 Happy Fish / YuQing
|
||||||
|
*
|
||||||
|
* FastDFS may be copied only under the terms of the GNU General
|
||||||
|
* Public License V3, which may be found in the FastDFS source kit.
|
||||||
|
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||||
|
**/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include "fdfs_client.h"
|
||||||
|
#include "fdfs_global.h"
|
||||||
|
#include "base64.h"
|
||||||
|
#include "sockopt.h"
|
||||||
|
#include "logger.h"
|
||||||
|
#include "fdfs_http_shared.h"
|
||||||
|
|
||||||
|
int writeToFileCallback(void *arg, const int64_t file_size, const char *data, \
|
||||||
|
const int current_size)
|
||||||
|
{
|
||||||
|
if (arg == NULL)
|
||||||
|
{
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fwrite(data, current_size, 1, (FILE *)arg) != 1)
|
||||||
|
{
|
||||||
|
return errno != 0 ? errno : EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int uploadFileCallback(void *arg, const int64_t file_size, int sock)
|
||||||
|
{
|
||||||
|
int64_t total_send_bytes;
|
||||||
|
char *filename;
|
||||||
|
|
||||||
|
if (arg == NULL)
|
||||||
|
{
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
filename = (char *)arg;
|
||||||
|
return tcpsendfile(sock, filename, file_size, \
|
||||||
|
g_fdfs_network_timeout, &total_send_bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
char *conf_filename;
|
||||||
|
char *local_filename;
|
||||||
|
ConnectionInfo *pTrackerServer;
|
||||||
|
ConnectionInfo *pStorageServer;
|
||||||
|
int result;
|
||||||
|
ConnectionInfo storageServer;
|
||||||
|
char group_name[FDFS_GROUP_NAME_MAX_LEN + 1];
|
||||||
|
char remote_filename[256];
|
||||||
|
char appender_filename[256];
|
||||||
|
FDFSMetaData meta_list[32];
|
||||||
|
int meta_count;
|
||||||
|
char token[32 + 1];
|
||||||
|
char file_id[128];
|
||||||
|
char file_url[256];
|
||||||
|
char szDatetime[20];
|
||||||
|
char szPortPart[16];
|
||||||
|
int url_len;
|
||||||
|
time_t ts;
|
||||||
|
int64_t file_offset;
|
||||||
|
int64_t file_size = 0;
|
||||||
|
int store_path_index;
|
||||||
|
FDFSFileInfo file_info;
|
||||||
|
int upload_type;
|
||||||
|
const char *file_ext_name;
|
||||||
|
struct stat stat_buf;
|
||||||
|
|
||||||
|
printf("This is FastDFS client test program v%d.%02d\n" \
|
||||||
|
"\nCopyright (C) 2008, Happy Fish / YuQing\n" \
|
||||||
|
"\nFastDFS may be copied only under the terms of the GNU General\n" \
|
||||||
|
"Public License V3, which may be found in the FastDFS source kit.\n" \
|
||||||
|
"Please visit the FastDFS Home Page http://www.csource.org/ \n" \
|
||||||
|
"for more detail.\n\n" \
|
||||||
|
, g_fdfs_version.major, g_fdfs_version.minor);
|
||||||
|
|
||||||
|
if (argc < 3)
|
||||||
|
{
|
||||||
|
printf("Usage: %s <config_file> <local_filename> " \
|
||||||
|
"[FILE | BUFF | CALLBACK]\n", argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_init();
|
||||||
|
g_log_context.log_level = LOG_DEBUG;
|
||||||
|
|
||||||
|
conf_filename = argv[1];
|
||||||
|
if ((result=fdfs_client_init(conf_filename)) != 0)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
pTrackerServer = tracker_get_connection();
|
||||||
|
if (pTrackerServer == NULL)
|
||||||
|
{
|
||||||
|
fdfs_client_destroy();
|
||||||
|
return errno != 0 ? errno : ECONNREFUSED;
|
||||||
|
}
|
||||||
|
|
||||||
|
local_filename = argv[2];
|
||||||
|
if (argc == 3)
|
||||||
|
{
|
||||||
|
upload_type = FDFS_UPLOAD_BY_FILE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (strcmp(argv[3], "BUFF") == 0)
|
||||||
|
{
|
||||||
|
upload_type = FDFS_UPLOAD_BY_BUFF;
|
||||||
|
}
|
||||||
|
else if (strcmp(argv[3], "CALLBACK") == 0)
|
||||||
|
{
|
||||||
|
upload_type = FDFS_UPLOAD_BY_CALLBACK;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
upload_type = FDFS_UPLOAD_BY_FILE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*group_name = '\0';
|
||||||
|
store_path_index = 0;
|
||||||
|
if ((result=tracker_query_storage_store(pTrackerServer, \
|
||||||
|
&storageServer, group_name, &store_path_index)) != 0)
|
||||||
|
{
|
||||||
|
fdfs_client_destroy();
|
||||||
|
printf("tracker_query_storage fail, " \
|
||||||
|
"error no: %d, error info: %s\n", \
|
||||||
|
result, STRERROR(result));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("group_name=%s, ip_addr=%s, port=%d\n", \
|
||||||
|
group_name, storageServer.ip_addr, \
|
||||||
|
storageServer.port);
|
||||||
|
|
||||||
|
if ((pStorageServer=tracker_connect_server(&storageServer, \
|
||||||
|
&result)) == NULL)
|
||||||
|
{
|
||||||
|
fdfs_client_destroy();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&meta_list, 0, sizeof(meta_list));
|
||||||
|
meta_count = 0;
|
||||||
|
strcpy(meta_list[meta_count].name, "ext_name");
|
||||||
|
strcpy(meta_list[meta_count].value, "jpg");
|
||||||
|
meta_count++;
|
||||||
|
strcpy(meta_list[meta_count].name, "width");
|
||||||
|
strcpy(meta_list[meta_count].value, "160");
|
||||||
|
meta_count++;
|
||||||
|
strcpy(meta_list[meta_count].name, "height");
|
||||||
|
strcpy(meta_list[meta_count].value, "80");
|
||||||
|
meta_count++;
|
||||||
|
strcpy(meta_list[meta_count].name, "file_size");
|
||||||
|
strcpy(meta_list[meta_count].value, "115120");
|
||||||
|
meta_count++;
|
||||||
|
|
||||||
|
file_ext_name = fdfs_get_file_ext_name(local_filename);
|
||||||
|
|
||||||
|
if (upload_type == FDFS_UPLOAD_BY_FILE)
|
||||||
|
{
|
||||||
|
if (stat(local_filename, &stat_buf) == 0 && \
|
||||||
|
S_ISREG(stat_buf.st_mode))
|
||||||
|
{
|
||||||
|
file_size = stat_buf.st_size;
|
||||||
|
result = storage_upload_appender_by_filename ( \
|
||||||
|
pTrackerServer, pStorageServer, \
|
||||||
|
store_path_index, local_filename, \
|
||||||
|
file_ext_name, meta_list, meta_count, \
|
||||||
|
group_name, remote_filename);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = errno != 0 ? errno : ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("storage_upload_appender_by_filename\n");
|
||||||
|
}
|
||||||
|
else if (upload_type == FDFS_UPLOAD_BY_BUFF)
|
||||||
|
{
|
||||||
|
char *file_content;
|
||||||
|
if ((result=getFileContent(local_filename, \
|
||||||
|
&file_content, &file_size)) == 0)
|
||||||
|
{
|
||||||
|
result = storage_upload_appender_by_filebuff( \
|
||||||
|
pTrackerServer, pStorageServer, \
|
||||||
|
store_path_index, file_content, \
|
||||||
|
file_size, file_ext_name, \
|
||||||
|
meta_list, meta_count, \
|
||||||
|
group_name, remote_filename);
|
||||||
|
free(file_content);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("storage_upload_appender_by_filebuff\n");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (stat(local_filename, &stat_buf) == 0 && \
|
||||||
|
S_ISREG(stat_buf.st_mode))
|
||||||
|
{
|
||||||
|
file_size = stat_buf.st_size;
|
||||||
|
result = storage_upload_appender_by_callback( \
|
||||||
|
pTrackerServer, pStorageServer, \
|
||||||
|
store_path_index, uploadFileCallback, \
|
||||||
|
local_filename, file_size, \
|
||||||
|
file_ext_name, meta_list, meta_count, \
|
||||||
|
group_name, remote_filename);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = errno != 0 ? errno : ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("storage_upload_appender_by_callback\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result != 0)
|
||||||
|
{
|
||||||
|
printf("upload file fail, " \
|
||||||
|
"error no: %d, error info: %s\n", \
|
||||||
|
result, STRERROR(result));
|
||||||
|
tracker_disconnect_server_ex(pStorageServer, true);
|
||||||
|
fdfs_client_destroy();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_tracker_server_http_port == 80)
|
||||||
|
{
|
||||||
|
*szPortPart = '\0';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sprintf(szPortPart, ":%d", g_tracker_server_http_port);
|
||||||
|
}
|
||||||
|
|
||||||
|
sprintf(file_id, "%s/%s", group_name, remote_filename);
|
||||||
|
url_len = sprintf(file_url, "http://%s%s/%s", \
|
||||||
|
pTrackerServer->ip_addr, szPortPart, file_id);
|
||||||
|
if (g_anti_steal_token)
|
||||||
|
{
|
||||||
|
ts = time(NULL);
|
||||||
|
fdfs_http_gen_token(&g_anti_steal_secret_key, file_id, \
|
||||||
|
ts, token);
|
||||||
|
sprintf(file_url + url_len, "?token=%s&ts=%d", token, (int)ts);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("group_name=%s, remote_filename=%s\n", \
|
||||||
|
group_name, remote_filename);
|
||||||
|
|
||||||
|
fdfs_get_file_info(group_name, remote_filename, &file_info);
|
||||||
|
printf("source ip address: %s\n", file_info.source_ip_addr);
|
||||||
|
printf("file timestamp=%s\n", formatDatetime(
|
||||||
|
file_info.create_timestamp, "%Y-%m-%d %H:%M:%S", \
|
||||||
|
szDatetime, sizeof(szDatetime)));
|
||||||
|
printf("file size="INT64_PRINTF_FORMAT"\n", file_info.file_size);
|
||||||
|
printf("file crc32=%u\n", file_info.crc32);
|
||||||
|
printf("file url: %s\n", file_url);
|
||||||
|
|
||||||
|
//sleep(90);
|
||||||
|
strcpy(appender_filename, remote_filename);
|
||||||
|
if (storage_truncate_file(pTrackerServer, pStorageServer, \
|
||||||
|
group_name, appender_filename, file_size / 2) != 0)
|
||||||
|
{
|
||||||
|
printf("truncate file fail, " \
|
||||||
|
"error no: %d, error info: %s\n", \
|
||||||
|
result, STRERROR(result));
|
||||||
|
tracker_disconnect_server_ex(pStorageServer, true);
|
||||||
|
fdfs_client_destroy();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
fdfs_get_file_info(group_name, appender_filename, &file_info);
|
||||||
|
printf("source ip address: %s\n", file_info.source_ip_addr);
|
||||||
|
printf("file timestamp=%s\n", formatDatetime(
|
||||||
|
file_info.create_timestamp, "%Y-%m-%d %H:%M:%S", \
|
||||||
|
szDatetime, sizeof(szDatetime)));
|
||||||
|
printf("file size="INT64_PRINTF_FORMAT"\n", file_info.file_size);
|
||||||
|
printf("file crc32=%u\n", file_info.crc32);
|
||||||
|
printf("file url: %s\n", file_url);
|
||||||
|
if (file_info.file_size != file_size / 2)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "file size: "INT64_PRINTF_FORMAT \
|
||||||
|
" != "INT64_PRINTF_FORMAT"!!!", file_info.file_size, file_size / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
//sleep(100);
|
||||||
|
if (upload_type == FDFS_UPLOAD_BY_FILE)
|
||||||
|
{
|
||||||
|
result = storage_append_by_filename(pTrackerServer, \
|
||||||
|
pStorageServer, local_filename,
|
||||||
|
group_name, appender_filename);
|
||||||
|
|
||||||
|
printf("storage_append_by_filename\n");
|
||||||
|
}
|
||||||
|
else if (upload_type == FDFS_UPLOAD_BY_BUFF)
|
||||||
|
{
|
||||||
|
char *file_content;
|
||||||
|
if ((result=getFileContent(local_filename, \
|
||||||
|
&file_content, &file_size)) == 0)
|
||||||
|
{
|
||||||
|
result = storage_append_by_filebuff(pTrackerServer, \
|
||||||
|
pStorageServer, file_content, \
|
||||||
|
file_size, group_name, appender_filename);
|
||||||
|
free(file_content);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("storage_append_by_filebuff\n");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (stat(local_filename, &stat_buf) == 0 && \
|
||||||
|
S_ISREG(stat_buf.st_mode))
|
||||||
|
{
|
||||||
|
file_size = stat_buf.st_size;
|
||||||
|
result = storage_append_by_callback(pTrackerServer, \
|
||||||
|
pStorageServer, uploadFileCallback, \
|
||||||
|
local_filename, file_size, \
|
||||||
|
group_name, appender_filename);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = errno != 0 ? errno : ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("storage_append_by_callback\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result != 0)
|
||||||
|
{
|
||||||
|
printf("append file fail, " \
|
||||||
|
"error no: %d, error info: %s\n", \
|
||||||
|
result, STRERROR(result));
|
||||||
|
tracker_disconnect_server_ex(pStorageServer, true);
|
||||||
|
fdfs_client_destroy();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
printf("append file successfully.\n");
|
||||||
|
fdfs_get_file_info(group_name, remote_filename, &file_info);
|
||||||
|
printf("source ip address: %s\n", file_info.source_ip_addr);
|
||||||
|
printf("file timestamp=%s\n", formatDatetime(
|
||||||
|
file_info.create_timestamp, "%Y-%m-%d %H:%M:%S", \
|
||||||
|
szDatetime, sizeof(szDatetime)));
|
||||||
|
printf("file size="INT64_PRINTF_FORMAT"\n", file_info.file_size);
|
||||||
|
if (file_info.file_size != file_size + file_size / 2)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "file size: "INT64_PRINTF_FORMAT \
|
||||||
|
" != "INT64_PRINTF_FORMAT"!!!", file_info.file_size, \
|
||||||
|
file_size + file_size / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
file_offset = file_info.file_size;
|
||||||
|
if (upload_type == FDFS_UPLOAD_BY_FILE)
|
||||||
|
{
|
||||||
|
result = storage_modify_by_filename(pTrackerServer, \
|
||||||
|
pStorageServer, local_filename, \
|
||||||
|
file_offset, group_name, \
|
||||||
|
appender_filename);
|
||||||
|
|
||||||
|
printf("storage_modify_by_filename\n");
|
||||||
|
}
|
||||||
|
else if (upload_type == FDFS_UPLOAD_BY_BUFF)
|
||||||
|
{
|
||||||
|
char *file_content;
|
||||||
|
if ((result=getFileContent(local_filename, \
|
||||||
|
&file_content, &file_size)) == 0)
|
||||||
|
{
|
||||||
|
result = storage_modify_by_filebuff(pTrackerServer, \
|
||||||
|
pStorageServer, file_content, \
|
||||||
|
file_offset, file_size, group_name, \
|
||||||
|
appender_filename);
|
||||||
|
free(file_content);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("storage_modify_by_filebuff\n");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (stat(local_filename, &stat_buf) == 0 && \
|
||||||
|
S_ISREG(stat_buf.st_mode))
|
||||||
|
{
|
||||||
|
file_size = stat_buf.st_size;
|
||||||
|
result = storage_modify_by_callback(pTrackerServer, \
|
||||||
|
pStorageServer, uploadFileCallback, \
|
||||||
|
local_filename, file_offset, \
|
||||||
|
file_size, group_name, appender_filename);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = errno != 0 ? errno : ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("storage_modify_by_callback\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result != 0)
|
||||||
|
{
|
||||||
|
printf("modify file fail, " \
|
||||||
|
"error no: %d, error info: %s\n", \
|
||||||
|
result, STRERROR(result));
|
||||||
|
tracker_disconnect_server_ex(pStorageServer, true);
|
||||||
|
fdfs_client_destroy();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
printf("modify file successfully.\n");
|
||||||
|
fdfs_get_file_info(group_name, remote_filename, &file_info);
|
||||||
|
printf("source ip address: %s\n", file_info.source_ip_addr);
|
||||||
|
printf("file timestamp=%s\n", formatDatetime(
|
||||||
|
file_info.create_timestamp, "%Y-%m-%d %H:%M:%S", \
|
||||||
|
szDatetime, sizeof(szDatetime)));
|
||||||
|
printf("file size="INT64_PRINTF_FORMAT"\n", file_info.file_size);
|
||||||
|
if (file_info.file_size != 2 * file_size + file_size / 2)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "file size: "INT64_PRINTF_FORMAT \
|
||||||
|
" != "INT64_PRINTF_FORMAT"!!!", file_info.file_size, \
|
||||||
|
2 * file_size + file_size /2);
|
||||||
|
}
|
||||||
|
|
||||||
|
tracker_disconnect_server_ex(pStorageServer, true);
|
||||||
|
tracker_disconnect_server_ex(pTrackerServer, true);
|
||||||
|
|
||||||
|
fdfs_client_destroy();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,435 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2008 Happy Fish / YuQing
|
||||||
|
*
|
||||||
|
* FastDFS may be copied only under the terms of the GNU General
|
||||||
|
* Public License V3, which may be found in the FastDFS source kit.
|
||||||
|
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||||
|
**/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include "fdfs_client.h"
|
||||||
|
#include "fdfs_global.h"
|
||||||
|
#include "base64.h"
|
||||||
|
#include "sockopt.h"
|
||||||
|
#include "logger.h"
|
||||||
|
#include "fdfs_http_shared.h"
|
||||||
|
|
||||||
|
int writeToFileCallback(void *arg, const int64_t file_size, const char *data, \
|
||||||
|
const int current_size)
|
||||||
|
{
|
||||||
|
if (arg == NULL)
|
||||||
|
{
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fwrite(data, current_size, 1, (FILE *)arg) != 1)
|
||||||
|
{
|
||||||
|
return errno != 0 ? errno : EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int uploadFileCallback(void *arg, const int64_t file_size, int sock)
|
||||||
|
{
|
||||||
|
int64_t total_send_bytes;
|
||||||
|
char *filename;
|
||||||
|
|
||||||
|
if (arg == NULL)
|
||||||
|
{
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
filename = (char *)arg;
|
||||||
|
return tcpsendfile(sock, filename, file_size, \
|
||||||
|
g_fdfs_network_timeout, &total_send_bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
char *conf_filename;
|
||||||
|
char *local_filename;
|
||||||
|
ConnectionInfo *pTrackerServer;
|
||||||
|
ConnectionInfo *pStorageServer;
|
||||||
|
int result;
|
||||||
|
ConnectionInfo storageServer;
|
||||||
|
char group_name[FDFS_GROUP_NAME_MAX_LEN + 1];
|
||||||
|
char file_id[256];
|
||||||
|
char appender_file_id[256];
|
||||||
|
FDFSMetaData meta_list[32];
|
||||||
|
int meta_count;
|
||||||
|
char token[32 + 1];
|
||||||
|
char file_url[256];
|
||||||
|
char szDatetime[20];
|
||||||
|
char szPortPart[16];
|
||||||
|
int url_len;
|
||||||
|
time_t ts;
|
||||||
|
int64_t file_offset;
|
||||||
|
int64_t file_size = 0;
|
||||||
|
int store_path_index;
|
||||||
|
FDFSFileInfo file_info;
|
||||||
|
int upload_type;
|
||||||
|
const char *file_ext_name;
|
||||||
|
struct stat stat_buf;
|
||||||
|
|
||||||
|
printf("This is FastDFS client test program v%d.%02d\n" \
|
||||||
|
"\nCopyright (C) 2008, Happy Fish / YuQing\n" \
|
||||||
|
"\nFastDFS may be copied only under the terms of the GNU General\n" \
|
||||||
|
"Public License V3, which may be found in the FastDFS source kit.\n" \
|
||||||
|
"Please visit the FastDFS Home Page http://www.csource.org/ \n" \
|
||||||
|
"for more detail.\n\n" \
|
||||||
|
, g_fdfs_version.major, g_fdfs_version.minor);
|
||||||
|
|
||||||
|
if (argc < 3)
|
||||||
|
{
|
||||||
|
printf("Usage: %s <config_file> <local_filename> " \
|
||||||
|
"[FILE | BUFF | CALLBACK]\n", argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_init();
|
||||||
|
g_log_context.log_level = LOG_DEBUG;
|
||||||
|
|
||||||
|
conf_filename = argv[1];
|
||||||
|
if ((result=fdfs_client_init(conf_filename)) != 0)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
pTrackerServer = tracker_get_connection();
|
||||||
|
if (pTrackerServer == NULL)
|
||||||
|
{
|
||||||
|
fdfs_client_destroy();
|
||||||
|
return errno != 0 ? errno : ECONNREFUSED;
|
||||||
|
}
|
||||||
|
|
||||||
|
local_filename = argv[2];
|
||||||
|
if (argc == 3)
|
||||||
|
{
|
||||||
|
upload_type = FDFS_UPLOAD_BY_FILE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (strcmp(argv[3], "BUFF") == 0)
|
||||||
|
{
|
||||||
|
upload_type = FDFS_UPLOAD_BY_BUFF;
|
||||||
|
}
|
||||||
|
else if (strcmp(argv[3], "CALLBACK") == 0)
|
||||||
|
{
|
||||||
|
upload_type = FDFS_UPLOAD_BY_CALLBACK;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
upload_type = FDFS_UPLOAD_BY_FILE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
store_path_index = 0;
|
||||||
|
*group_name = '\0';
|
||||||
|
if ((result=tracker_query_storage_store(pTrackerServer, \
|
||||||
|
&storageServer, group_name, &store_path_index)) != 0)
|
||||||
|
{
|
||||||
|
fdfs_client_destroy();
|
||||||
|
printf("tracker_query_storage fail, " \
|
||||||
|
"error no: %d, error info: %s\n", \
|
||||||
|
result, STRERROR(result));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("group_name=%s, ip_addr=%s, port=%d\n", \
|
||||||
|
group_name, storageServer.ip_addr, \
|
||||||
|
storageServer.port);
|
||||||
|
|
||||||
|
if ((pStorageServer=tracker_connect_server(&storageServer, \
|
||||||
|
&result)) == NULL)
|
||||||
|
{
|
||||||
|
fdfs_client_destroy();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&meta_list, 0, sizeof(meta_list));
|
||||||
|
meta_count = 0;
|
||||||
|
strcpy(meta_list[meta_count].name, "ext_name");
|
||||||
|
strcpy(meta_list[meta_count].value, "jpg");
|
||||||
|
meta_count++;
|
||||||
|
strcpy(meta_list[meta_count].name, "width");
|
||||||
|
strcpy(meta_list[meta_count].value, "160");
|
||||||
|
meta_count++;
|
||||||
|
strcpy(meta_list[meta_count].name, "height");
|
||||||
|
strcpy(meta_list[meta_count].value, "80");
|
||||||
|
meta_count++;
|
||||||
|
strcpy(meta_list[meta_count].name, "file_size");
|
||||||
|
strcpy(meta_list[meta_count].value, "115120");
|
||||||
|
meta_count++;
|
||||||
|
|
||||||
|
file_ext_name = fdfs_get_file_ext_name(local_filename);
|
||||||
|
if (upload_type == FDFS_UPLOAD_BY_FILE)
|
||||||
|
{
|
||||||
|
if (stat(local_filename, &stat_buf) == 0 && \
|
||||||
|
S_ISREG(stat_buf.st_mode))
|
||||||
|
{
|
||||||
|
file_size = stat_buf.st_size;
|
||||||
|
result = storage_upload_appender_by_filename1( \
|
||||||
|
pTrackerServer, pStorageServer, \
|
||||||
|
store_path_index, local_filename, \
|
||||||
|
file_ext_name, meta_list, meta_count, \
|
||||||
|
group_name, file_id);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = errno != 0 ? errno : ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("storage_upload_appender_by_filename1\n");
|
||||||
|
}
|
||||||
|
else if (upload_type == FDFS_UPLOAD_BY_BUFF)
|
||||||
|
{
|
||||||
|
char *file_content;
|
||||||
|
if ((result=getFileContent(local_filename, \
|
||||||
|
&file_content, &file_size)) == 0)
|
||||||
|
{
|
||||||
|
result = storage_upload_appender_by_filebuff1( \
|
||||||
|
pTrackerServer, pStorageServer, \
|
||||||
|
store_path_index, file_content, \
|
||||||
|
file_size, file_ext_name, \
|
||||||
|
meta_list, meta_count, \
|
||||||
|
group_name, file_id);
|
||||||
|
free(file_content);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("storage_upload_appender_by_filebuff1\n");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (stat(local_filename, &stat_buf) == 0 && \
|
||||||
|
S_ISREG(stat_buf.st_mode))
|
||||||
|
{
|
||||||
|
file_size = stat_buf.st_size;
|
||||||
|
result = storage_upload_appender_by_callback1( \
|
||||||
|
pTrackerServer, pStorageServer, \
|
||||||
|
store_path_index, uploadFileCallback, \
|
||||||
|
local_filename, file_size, \
|
||||||
|
file_ext_name, meta_list, meta_count, \
|
||||||
|
group_name, file_id);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = errno != 0 ? errno : ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("storage_upload_appender_by_callback1\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result != 0)
|
||||||
|
{
|
||||||
|
printf("upload file fail, " \
|
||||||
|
"error no: %d, error info: %s\n", \
|
||||||
|
result, STRERROR(result));
|
||||||
|
tracker_disconnect_server_ex(pStorageServer, true);
|
||||||
|
fdfs_client_destroy();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_tracker_server_http_port == 80)
|
||||||
|
{
|
||||||
|
*szPortPart = '\0';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sprintf(szPortPart, ":%d", g_tracker_server_http_port);
|
||||||
|
}
|
||||||
|
|
||||||
|
url_len = sprintf(file_url, "http://%s%s/%s", \
|
||||||
|
pTrackerServer->ip_addr, szPortPart, file_id);
|
||||||
|
if (g_anti_steal_token)
|
||||||
|
{
|
||||||
|
ts = time(NULL);
|
||||||
|
fdfs_http_gen_token(&g_anti_steal_secret_key, file_id, \
|
||||||
|
ts, token);
|
||||||
|
sprintf(file_url + url_len, "?token=%s&ts=%d", token, (int)ts);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("fild_id=%s\n", file_id);
|
||||||
|
|
||||||
|
fdfs_get_file_info1(file_id, &file_info);
|
||||||
|
printf("source ip address: %s\n", file_info.source_ip_addr);
|
||||||
|
printf("file timestamp=%s\n", formatDatetime(
|
||||||
|
file_info.create_timestamp, "%Y-%m-%d %H:%M:%S", \
|
||||||
|
szDatetime, sizeof(szDatetime)));
|
||||||
|
printf("file size="INT64_PRINTF_FORMAT"\n", file_info.file_size);
|
||||||
|
printf("file crc32=%u\n", file_info.crc32);
|
||||||
|
printf("file url: %s\n", file_url);
|
||||||
|
|
||||||
|
|
||||||
|
strcpy(appender_file_id, file_id);
|
||||||
|
if (storage_truncate_file1(pTrackerServer, pStorageServer, \
|
||||||
|
appender_file_id, 0) != 0)
|
||||||
|
{
|
||||||
|
printf("truncate file fail, " \
|
||||||
|
"error no: %d, error info: %s\n", \
|
||||||
|
result, STRERROR(result));
|
||||||
|
tracker_disconnect_server_ex(pStorageServer, true);
|
||||||
|
fdfs_client_destroy();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
fdfs_get_file_info1(file_id, &file_info);
|
||||||
|
printf("source ip address: %s\n", file_info.source_ip_addr);
|
||||||
|
printf("file timestamp=%s\n", formatDatetime(
|
||||||
|
file_info.create_timestamp, "%Y-%m-%d %H:%M:%S", \
|
||||||
|
szDatetime, sizeof(szDatetime)));
|
||||||
|
printf("file size="INT64_PRINTF_FORMAT"\n", file_info.file_size);
|
||||||
|
printf("file crc32=%u\n", file_info.crc32);
|
||||||
|
printf("file url: %s\n", file_url);
|
||||||
|
if (file_info.file_size != 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "file size: "INT64_PRINTF_FORMAT \
|
||||||
|
" != 0!!!", file_info.file_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
//sleep(70);
|
||||||
|
if (upload_type == FDFS_UPLOAD_BY_FILE)
|
||||||
|
{
|
||||||
|
result = storage_append_by_filename1(pTrackerServer, \
|
||||||
|
pStorageServer, local_filename,
|
||||||
|
appender_file_id);
|
||||||
|
|
||||||
|
printf("storage_append_by_filename\n");
|
||||||
|
}
|
||||||
|
else if (upload_type == FDFS_UPLOAD_BY_BUFF)
|
||||||
|
{
|
||||||
|
char *file_content;
|
||||||
|
if ((result=getFileContent(local_filename, \
|
||||||
|
&file_content, &file_size)) == 0)
|
||||||
|
{
|
||||||
|
result = storage_append_by_filebuff1(pTrackerServer, \
|
||||||
|
pStorageServer, file_content, \
|
||||||
|
file_size, appender_file_id);
|
||||||
|
free(file_content);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("storage_append_by_filebuff1\n");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (stat(local_filename, &stat_buf) == 0 && \
|
||||||
|
S_ISREG(stat_buf.st_mode))
|
||||||
|
{
|
||||||
|
file_size = stat_buf.st_size;
|
||||||
|
result = storage_append_by_callback1(pTrackerServer, \
|
||||||
|
pStorageServer, uploadFileCallback, \
|
||||||
|
local_filename, file_size, \
|
||||||
|
appender_file_id);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = errno != 0 ? errno : ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("storage_append_by_callback1\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result != 0)
|
||||||
|
{
|
||||||
|
printf("append file fail, " \
|
||||||
|
"error no: %d, error info: %s\n", \
|
||||||
|
result, STRERROR(result));
|
||||||
|
tracker_disconnect_server_ex(pStorageServer, true);
|
||||||
|
fdfs_client_destroy();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
printf("append file successfully.\n");
|
||||||
|
fdfs_get_file_info1(appender_file_id, &file_info);
|
||||||
|
printf("source ip address: %s\n", file_info.source_ip_addr);
|
||||||
|
printf("file timestamp=%s\n", formatDatetime(
|
||||||
|
file_info.create_timestamp, "%Y-%m-%d %H:%M:%S", \
|
||||||
|
szDatetime, sizeof(szDatetime)));
|
||||||
|
printf("file size="INT64_PRINTF_FORMAT"\n", file_info.file_size);
|
||||||
|
if (file_info.file_size != file_size)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "file size: "INT64_PRINTF_FORMAT \
|
||||||
|
" != "INT64_PRINTF_FORMAT"!!!", file_info.file_size, \
|
||||||
|
file_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
file_offset = file_size;
|
||||||
|
if (upload_type == FDFS_UPLOAD_BY_FILE)
|
||||||
|
{
|
||||||
|
result = storage_modify_by_filename1(pTrackerServer, \
|
||||||
|
pStorageServer, local_filename,
|
||||||
|
file_offset, appender_file_id);
|
||||||
|
|
||||||
|
printf("storage_modify_by_filename\n");
|
||||||
|
}
|
||||||
|
else if (upload_type == FDFS_UPLOAD_BY_BUFF)
|
||||||
|
{
|
||||||
|
char *file_content;
|
||||||
|
if ((result=getFileContent(local_filename, \
|
||||||
|
&file_content, &file_size)) == 0)
|
||||||
|
{
|
||||||
|
result = storage_modify_by_filebuff1( \
|
||||||
|
pTrackerServer, pStorageServer, \
|
||||||
|
file_content, file_offset, file_size, \
|
||||||
|
appender_file_id);
|
||||||
|
free(file_content);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("storage_modify_by_filebuff1\n");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (stat(local_filename, &stat_buf) == 0 && \
|
||||||
|
S_ISREG(stat_buf.st_mode))
|
||||||
|
{
|
||||||
|
file_size = stat_buf.st_size;
|
||||||
|
result = storage_modify_by_callback1( \
|
||||||
|
pTrackerServer, pStorageServer, \
|
||||||
|
uploadFileCallback, \
|
||||||
|
local_filename, file_offset, \
|
||||||
|
file_size, appender_file_id);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = errno != 0 ? errno : ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("storage_modify_by_callback1\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result != 0)
|
||||||
|
{
|
||||||
|
printf("modify file fail, " \
|
||||||
|
"error no: %d, error info: %s\n", \
|
||||||
|
result, STRERROR(result));
|
||||||
|
tracker_disconnect_server_ex(pStorageServer, true);
|
||||||
|
fdfs_client_destroy();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
printf("modify file successfully.\n");
|
||||||
|
fdfs_get_file_info1(appender_file_id, &file_info);
|
||||||
|
printf("source ip address: %s\n", file_info.source_ip_addr);
|
||||||
|
printf("file timestamp=%s\n", formatDatetime(
|
||||||
|
file_info.create_timestamp, "%Y-%m-%d %H:%M:%S", \
|
||||||
|
szDatetime, sizeof(szDatetime)));
|
||||||
|
printf("file size="INT64_PRINTF_FORMAT"\n", file_info.file_size);
|
||||||
|
if (file_info.file_size != 2 * file_size)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "file size: "INT64_PRINTF_FORMAT \
|
||||||
|
" != "INT64_PRINTF_FORMAT"!!!", file_info.file_size, \
|
||||||
|
2 * file_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
tracker_disconnect_server_ex(pStorageServer, true);
|
||||||
|
tracker_disconnect_server_ex(pTrackerServer, true);
|
||||||
|
|
||||||
|
fdfs_client_destroy();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2008 Happy Fish / YuQing
|
||||||
|
*
|
||||||
|
* FastDFS may be copied only under the terms of the GNU General
|
||||||
|
* Public License V3, which may be found in the FastDFS source kit.
|
||||||
|
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||||
|
**/
|
||||||
|
|
||||||
|
#ifndef FDFS_CLIENT_H
|
||||||
|
#define FDFS_CLIENT_H
|
||||||
|
|
||||||
|
#include "shared_func.h"
|
||||||
|
#include "tracker_types.h"
|
||||||
|
#include "tracker_proto.h"
|
||||||
|
#include "tracker_client.h"
|
||||||
|
#include "storage_client.h"
|
||||||
|
#include "storage_client1.h"
|
||||||
|
#include "client_func.h"
|
||||||
|
#include "client_global.h"
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
@ -0,0 +1,105 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2008 Happy Fish / YuQing
|
||||||
|
*
|
||||||
|
* FastDFS may be copied only under the terms of the GNU General
|
||||||
|
* Public License V3, which may be found in the FastDFS source kit.
|
||||||
|
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||||
|
**/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include "hash.h"
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
int64_t file_size;
|
||||||
|
int64_t remain_bytes;
|
||||||
|
char *filename;
|
||||||
|
int fd;
|
||||||
|
int read_bytes;
|
||||||
|
int result;
|
||||||
|
int crc32;
|
||||||
|
char buff[512 * 1024];
|
||||||
|
|
||||||
|
if (argc < 2)
|
||||||
|
{
|
||||||
|
printf("Usage: %s <filename>\n", argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
filename = argv[1];
|
||||||
|
fd = open(filename, O_RDONLY);
|
||||||
|
if (fd < 0)
|
||||||
|
{
|
||||||
|
printf("file: "__FILE__", line: %d, " \
|
||||||
|
"open file %s fail, " \
|
||||||
|
"errno: %d, error info: %s\n", \
|
||||||
|
__LINE__, filename, errno, STRERROR(errno));
|
||||||
|
return errno != 0 ? errno : EACCES;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((file_size=lseek(fd, 0, SEEK_END)) < 0)
|
||||||
|
{
|
||||||
|
printf("file: "__FILE__", line: %d, " \
|
||||||
|
"call lseek fail, " \
|
||||||
|
"errno: %d, error info: %s\n", \
|
||||||
|
__LINE__, errno, STRERROR(errno));
|
||||||
|
close(fd);
|
||||||
|
return errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lseek(fd, 0, SEEK_SET) < 0)
|
||||||
|
{
|
||||||
|
printf("file: "__FILE__", line: %d, " \
|
||||||
|
"call lseek fail, " \
|
||||||
|
"errno: %d, error info: %s\n", \
|
||||||
|
__LINE__, errno, STRERROR(errno));
|
||||||
|
close(fd);
|
||||||
|
return errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
crc32 = CRC32_XINIT;
|
||||||
|
result = 0;
|
||||||
|
remain_bytes = file_size;
|
||||||
|
while (remain_bytes > 0)
|
||||||
|
{
|
||||||
|
if (remain_bytes > sizeof(buff))
|
||||||
|
{
|
||||||
|
read_bytes = sizeof(buff);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
read_bytes = remain_bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (read(fd, buff, read_bytes) != read_bytes)
|
||||||
|
{
|
||||||
|
printf("file: "__FILE__", line: %d, " \
|
||||||
|
"call lseek fail, " \
|
||||||
|
"errno: %d, error info: %s\n", \
|
||||||
|
__LINE__, errno, STRERROR(errno));
|
||||||
|
result = errno != 0 ? errno : EIO;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
crc32 = CRC32_ex(buff, read_bytes, crc32);
|
||||||
|
remain_bytes -= read_bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
if (result == 0)
|
||||||
|
{
|
||||||
|
crc32 = CRC32_FINAL(crc32);
|
||||||
|
printf("%u\n", crc32);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2008 Happy Fish / YuQing
|
||||||
|
*
|
||||||
|
* FastDFS may be copied only under the terms of the GNU General
|
||||||
|
* Public License V3, which may be found in the FastDFS source kit.
|
||||||
|
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||||
|
**/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include "fdfs_client.h"
|
||||||
|
#include "logger.h"
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
char *conf_filename;
|
||||||
|
ConnectionInfo *pTrackerServer;
|
||||||
|
int result;
|
||||||
|
char file_id[128];
|
||||||
|
|
||||||
|
if (argc < 3)
|
||||||
|
{
|
||||||
|
printf("Usage: %s <config_file> <file_id>\n", argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_init();
|
||||||
|
g_log_context.log_level = LOG_ERR;
|
||||||
|
ignore_signal_pipe();
|
||||||
|
|
||||||
|
conf_filename = argv[1];
|
||||||
|
if ((result=fdfs_client_init(conf_filename)) != 0)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
pTrackerServer = tracker_get_connection();
|
||||||
|
if (pTrackerServer == NULL)
|
||||||
|
{
|
||||||
|
fdfs_client_destroy();
|
||||||
|
return errno != 0 ? errno : ECONNREFUSED;
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(file_id, sizeof(file_id), "%s", argv[2]);
|
||||||
|
if ((result=storage_delete_file1(pTrackerServer, NULL, file_id)) != 0)
|
||||||
|
{
|
||||||
|
printf("delete file fail, " \
|
||||||
|
"error no: %d, error info: %s\n", \
|
||||||
|
result, STRERROR(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
tracker_disconnect_server_ex(pTrackerServer, true);
|
||||||
|
fdfs_client_destroy();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,97 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2008 Happy Fish / YuQing
|
||||||
|
*
|
||||||
|
* FastDFS may be copied only under the terms of the GNU General
|
||||||
|
* Public License V3, which may be found in the FastDFS source kit.
|
||||||
|
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||||
|
**/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include "fdfs_client.h"
|
||||||
|
#include "logger.h"
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
char *conf_filename;
|
||||||
|
char *local_filename;
|
||||||
|
ConnectionInfo *pTrackerServer;
|
||||||
|
int result;
|
||||||
|
char file_id[128];
|
||||||
|
int64_t file_size;
|
||||||
|
int64_t file_offset;
|
||||||
|
int64_t download_bytes;
|
||||||
|
|
||||||
|
if (argc < 3)
|
||||||
|
{
|
||||||
|
printf("Usage: %s <config_file> <file_id> " \
|
||||||
|
"[local_filename] [<download_offset> " \
|
||||||
|
"<download_bytes>]\n", argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_init();
|
||||||
|
g_log_context.log_level = LOG_ERR;
|
||||||
|
ignore_signal_pipe();
|
||||||
|
|
||||||
|
conf_filename = argv[1];
|
||||||
|
if ((result=fdfs_client_init(conf_filename)) != 0)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
pTrackerServer = tracker_get_connection();
|
||||||
|
if (pTrackerServer == NULL)
|
||||||
|
{
|
||||||
|
fdfs_client_destroy();
|
||||||
|
return errno != 0 ? errno : ECONNREFUSED;
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(file_id, sizeof(file_id), "%s", argv[2]);
|
||||||
|
|
||||||
|
file_offset = 0;
|
||||||
|
download_bytes = 0;
|
||||||
|
if (argc >= 4)
|
||||||
|
{
|
||||||
|
local_filename = argv[3];
|
||||||
|
if (argc >= 6)
|
||||||
|
{
|
||||||
|
file_offset = strtoll(argv[4], NULL, 10);
|
||||||
|
download_bytes = strtoll(argv[5], NULL, 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
local_filename = strrchr(file_id, '/');
|
||||||
|
if (local_filename != NULL)
|
||||||
|
{
|
||||||
|
local_filename++; //skip /
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
local_filename = file_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result = storage_do_download_file1_ex(pTrackerServer, \
|
||||||
|
NULL, FDFS_DOWNLOAD_TO_FILE, file_id, \
|
||||||
|
file_offset, download_bytes, \
|
||||||
|
&local_filename, NULL, &file_size);
|
||||||
|
if (result != 0)
|
||||||
|
{
|
||||||
|
printf("download file fail, " \
|
||||||
|
"error no: %d, error info: %s\n", \
|
||||||
|
result, STRERROR(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
tracker_disconnect_server_ex(pTrackerServer, true);
|
||||||
|
fdfs_client_destroy();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,71 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2008 Happy Fish / YuQing
|
||||||
|
*
|
||||||
|
* FastDFS may be copied only under the terms of the GNU General
|
||||||
|
* Public License V3, which may be found in the FastDFS source kit.
|
||||||
|
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||||
|
**/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include "fdfs_client.h"
|
||||||
|
#include "logger.h"
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
char *conf_filename;
|
||||||
|
char file_id[128];
|
||||||
|
int result;
|
||||||
|
FDFSFileInfo file_info;
|
||||||
|
|
||||||
|
if (argc < 3)
|
||||||
|
{
|
||||||
|
printf("Usage: %s <config_file> <file_id>\n", argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_init();
|
||||||
|
g_log_context.log_level = LOG_ERR;
|
||||||
|
ignore_signal_pipe();
|
||||||
|
|
||||||
|
conf_filename = argv[1];
|
||||||
|
if ((result=fdfs_client_init(conf_filename)) != 0)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(file_id, sizeof(file_id), "%s", argv[2]);
|
||||||
|
memset(&file_info, 0, sizeof(file_info));
|
||||||
|
result = fdfs_get_file_info_ex1(file_id, true, &file_info);
|
||||||
|
if (result != 0)
|
||||||
|
{
|
||||||
|
printf("query file info fail, " \
|
||||||
|
"error no: %d, error info: %s\n", \
|
||||||
|
result, STRERROR(result));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
char szDatetime[32];
|
||||||
|
|
||||||
|
printf("source storage id: %d\n", file_info.source_id);
|
||||||
|
printf("source ip address: %s\n", file_info.source_ip_addr);
|
||||||
|
printf("file create timestamp: %s\n", formatDatetime(
|
||||||
|
file_info.create_timestamp, "%Y-%m-%d %H:%M:%S", \
|
||||||
|
szDatetime, sizeof(szDatetime)));
|
||||||
|
printf("file size: "INT64_PRINTF_FORMAT"\n", \
|
||||||
|
file_info.file_size);
|
||||||
|
printf("file crc32: %u (0x%08X)\n", \
|
||||||
|
file_info.crc32, file_info.crc32);
|
||||||
|
}
|
||||||
|
|
||||||
|
tracker_close_all_connections();
|
||||||
|
fdfs_client_destroy();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
tmp_src_filename=_fdfs_check_bits_.c
|
||||||
|
cat <<EOF > $tmp_src_filename
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
printf("%d\n", (int)sizeof(long));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
gcc -D_FILE_OFFSET_BITS=64 -o a.out $tmp_src_filename
|
||||||
|
OS_BITS=`./a.out`
|
||||||
|
|
||||||
|
rm $tmp_src_filename a.out
|
||||||
|
|
||||||
|
TARGET_LIB="/usr/local/lib"
|
||||||
|
if [ "`id -u`" = "0" ]; then
|
||||||
|
ln -fs $TARGET_LIB/libfastcommon.so.1 /usr/lib/libfastcommon.so
|
||||||
|
ln -fs $TARGET_LIB/libfdfsclient.so.1 /usr/lib/libfdfsclient.so
|
||||||
|
|
||||||
|
if [ "$OS_BITS" = "8" ]; then
|
||||||
|
ln -fs $TARGET_LIB/libfastcommon.so.1 /usr/lib64/libfastcommon.so
|
||||||
|
ln -fs $TARGET_LIB/libfdfsclient.so.1 /usr/lib64/libfdfsclient.so
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
tmp_src_filename=_fdfs_check_bits_.c
|
||||||
|
cat <<EOF > $tmp_src_filename
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
printf("%d\n", (int)sizeof(long));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
gcc -D_FILE_OFFSET_BITS=64 -o a.out $tmp_src_filename
|
||||||
|
OS_BITS=`./a.out`
|
||||||
|
|
||||||
|
rm $tmp_src_filename a.out
|
||||||
|
|
||||||
|
TARGET_LIB="$(TARGET_PREFIX)/lib"
|
||||||
|
if [ "`id -u`" = "0" ]; then
|
||||||
|
ln -fs $TARGET_LIB/libfastcommon.so.1 /usr/lib/libfastcommon.so
|
||||||
|
ln -fs $TARGET_LIB/libfdfsclient.so.1 /usr/lib/libfdfsclient.so
|
||||||
|
|
||||||
|
if [ "$OS_BITS" = "8" ]; then
|
||||||
|
ln -fs $TARGET_LIB/libfastcommon.so.1 /usr/lib64/libfastcommon.so
|
||||||
|
ln -fs $TARGET_LIB/libfdfsclient.so.1 /usr/lib64/libfdfsclient.so
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
@ -0,0 +1,571 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2008 Happy Fish / YuQing
|
||||||
|
*
|
||||||
|
* FastDFS may be copied only under the terms of the GNU General
|
||||||
|
* Public License V3, which may be found in the FastDFS source kit.
|
||||||
|
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||||
|
**/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include "sockopt.h"
|
||||||
|
#include "logger.h"
|
||||||
|
#include "client_global.h"
|
||||||
|
#include "fdfs_global.h"
|
||||||
|
#include "fdfs_client.h"
|
||||||
|
|
||||||
|
static ConnectionInfo *pTrackerServer;
|
||||||
|
|
||||||
|
static int list_all_groups(const char *group_name);
|
||||||
|
|
||||||
|
static void usage(char *argv[])
|
||||||
|
{
|
||||||
|
printf("Usage: %s <config_file> [-h <tracker_server>] [list|delete|set_trunk_server <group_name> " \
|
||||||
|
"[storage_id]]\n", argv[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
char *conf_filename;
|
||||||
|
int result;
|
||||||
|
char *op_type;
|
||||||
|
char *tracker_server;
|
||||||
|
int arg_index;
|
||||||
|
char *group_name;
|
||||||
|
|
||||||
|
if (argc < 2)
|
||||||
|
{
|
||||||
|
usage(argv);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
tracker_server = NULL;
|
||||||
|
conf_filename = argv[1];
|
||||||
|
arg_index = 2;
|
||||||
|
|
||||||
|
if (arg_index >= argc)
|
||||||
|
{
|
||||||
|
op_type = "list";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int len;
|
||||||
|
|
||||||
|
len = strlen(argv[arg_index]);
|
||||||
|
if (len >= 2 && strncmp(argv[arg_index], "-h", 2) == 0)
|
||||||
|
{
|
||||||
|
if (len == 2)
|
||||||
|
{
|
||||||
|
arg_index++;
|
||||||
|
if (arg_index >= argc)
|
||||||
|
{
|
||||||
|
usage(argv);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
tracker_server = argv[arg_index++];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tracker_server = argv[arg_index] + 2;
|
||||||
|
arg_index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg_index < argc)
|
||||||
|
{
|
||||||
|
op_type = argv[arg_index++];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
op_type = "list";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
op_type = argv[arg_index++];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log_init();
|
||||||
|
g_log_context.log_level = LOG_DEBUG;
|
||||||
|
ignore_signal_pipe();
|
||||||
|
|
||||||
|
if ((result=fdfs_client_init(conf_filename)) != 0)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
load_log_level_ex(conf_filename);
|
||||||
|
|
||||||
|
if (tracker_server == NULL)
|
||||||
|
{
|
||||||
|
if (g_tracker_group.server_count > 1)
|
||||||
|
{
|
||||||
|
srand(time(NULL));
|
||||||
|
rand(); //discard the first
|
||||||
|
g_tracker_group.server_index = (int)( \
|
||||||
|
(g_tracker_group.server_count * (double)rand()) \
|
||||||
|
/ (double)RAND_MAX);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
char ip_addr[IP_ADDRESS_SIZE];
|
||||||
|
|
||||||
|
*ip_addr = '\0';
|
||||||
|
if (getIpaddrByName(tracker_server, ip_addr, sizeof(ip_addr)) \
|
||||||
|
== INADDR_NONE)
|
||||||
|
{
|
||||||
|
printf("resolve ip address of tracker server: %s " \
|
||||||
|
"fail!\n", tracker_server);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i=0; i<g_tracker_group.server_count; i++)
|
||||||
|
{
|
||||||
|
if (strcmp(g_tracker_group.servers[i].ip_addr, \
|
||||||
|
ip_addr) == 0)
|
||||||
|
{
|
||||||
|
g_tracker_group.server_index = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == g_tracker_group.server_count)
|
||||||
|
{
|
||||||
|
printf("tracker server: %s not exists!\n", tracker_server);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("server_count=%d, server_index=%d\n", g_tracker_group.server_count, g_tracker_group.server_index);
|
||||||
|
|
||||||
|
pTrackerServer = tracker_get_connection();
|
||||||
|
if (pTrackerServer == NULL)
|
||||||
|
{
|
||||||
|
fdfs_client_destroy();
|
||||||
|
return errno != 0 ? errno : ECONNREFUSED;
|
||||||
|
}
|
||||||
|
printf("\ntracker server is %s:%d\n\n", pTrackerServer->ip_addr, pTrackerServer->port);
|
||||||
|
|
||||||
|
if (arg_index < argc)
|
||||||
|
{
|
||||||
|
group_name = argv[arg_index++];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
group_name = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(op_type, "list") == 0)
|
||||||
|
{
|
||||||
|
if (group_name == NULL)
|
||||||
|
{
|
||||||
|
result = list_all_groups(NULL);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = list_all_groups(group_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (strcmp(op_type, "delete") == 0)
|
||||||
|
{
|
||||||
|
char *storage_id;
|
||||||
|
if (arg_index >= argc)
|
||||||
|
{
|
||||||
|
usage(argv);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
storage_id = argv[arg_index++];
|
||||||
|
|
||||||
|
if ((result=tracker_delete_storage(&g_tracker_group, \
|
||||||
|
group_name, storage_id)) == 0)
|
||||||
|
{
|
||||||
|
printf("delete storage server %s::%s success\n", \
|
||||||
|
group_name, storage_id);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("delete storage server %s::%s fail, " \
|
||||||
|
"error no: %d, error info: %s\n", \
|
||||||
|
group_name, storage_id, \
|
||||||
|
result, STRERROR(result));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (strcmp(op_type, "set_trunk_server") == 0)
|
||||||
|
{
|
||||||
|
char *storage_id;
|
||||||
|
char new_trunk_server_id[FDFS_STORAGE_ID_MAX_SIZE];
|
||||||
|
|
||||||
|
if (group_name == NULL)
|
||||||
|
{
|
||||||
|
usage(argv);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (arg_index >= argc)
|
||||||
|
{
|
||||||
|
storage_id = "";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
storage_id = argv[arg_index++];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((result=tracker_set_trunk_server(&g_tracker_group, \
|
||||||
|
group_name, storage_id, new_trunk_server_id)) == 0)
|
||||||
|
{
|
||||||
|
printf("set trunk server %s::%s success, " \
|
||||||
|
"new trunk server: %s\n", group_name, \
|
||||||
|
storage_id, new_trunk_server_id);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("set trunk server %s::%s fail, " \
|
||||||
|
"error no: %d, error info: %s\n", \
|
||||||
|
group_name, storage_id, \
|
||||||
|
result, STRERROR(result));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("Invalid command %s\n\n", op_type);
|
||||||
|
usage(argv);
|
||||||
|
}
|
||||||
|
|
||||||
|
tracker_disconnect_server_ex(pTrackerServer, true);
|
||||||
|
fdfs_client_destroy();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int list_storages(FDFSGroupStat *pGroupStat)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
int storage_count;
|
||||||
|
FDFSStorageInfo storage_infos[FDFS_MAX_SERVERS_EACH_GROUP];
|
||||||
|
FDFSStorageInfo *p;
|
||||||
|
FDFSStorageInfo *pStorage;
|
||||||
|
FDFSStorageInfo *pStorageEnd;
|
||||||
|
FDFSStorageStat *pStorageStat;
|
||||||
|
char szJoinTime[32];
|
||||||
|
char szUpTime[32];
|
||||||
|
char szLastHeartBeatTime[32];
|
||||||
|
char szSrcUpdTime[32];
|
||||||
|
char szSyncUpdTime[32];
|
||||||
|
char szSyncedTimestamp[32];
|
||||||
|
char szSyncedDelaySeconds[128];
|
||||||
|
char szHostname[128];
|
||||||
|
char szHostnamePrompt[128+8];
|
||||||
|
int k;
|
||||||
|
int max_last_source_update;
|
||||||
|
|
||||||
|
printf( "group name = %s\n" \
|
||||||
|
"disk total space = "INT64_PRINTF_FORMAT" MB\n" \
|
||||||
|
"disk free space = "INT64_PRINTF_FORMAT" MB\n" \
|
||||||
|
"trunk free space = "INT64_PRINTF_FORMAT" MB\n" \
|
||||||
|
"storage server count = %d\n" \
|
||||||
|
"active server count = %d\n" \
|
||||||
|
"storage server port = %d\n" \
|
||||||
|
"storage HTTP port = %d\n" \
|
||||||
|
"store path count = %d\n" \
|
||||||
|
"subdir count per path = %d\n" \
|
||||||
|
"current write server index = %d\n" \
|
||||||
|
"current trunk file id = %d\n\n", \
|
||||||
|
pGroupStat->group_name, \
|
||||||
|
pGroupStat->total_mb, \
|
||||||
|
pGroupStat->free_mb, \
|
||||||
|
pGroupStat->trunk_free_mb, \
|
||||||
|
pGroupStat->count, \
|
||||||
|
pGroupStat->active_count, \
|
||||||
|
pGroupStat->storage_port, \
|
||||||
|
pGroupStat->storage_http_port, \
|
||||||
|
pGroupStat->store_path_count, \
|
||||||
|
pGroupStat->subdir_count_per_path, \
|
||||||
|
pGroupStat->current_write_server, \
|
||||||
|
pGroupStat->current_trunk_file_id
|
||||||
|
);
|
||||||
|
|
||||||
|
result = tracker_list_servers(pTrackerServer, \
|
||||||
|
pGroupStat->group_name, NULL, \
|
||||||
|
storage_infos, FDFS_MAX_SERVERS_EACH_GROUP, \
|
||||||
|
&storage_count);
|
||||||
|
if (result != 0)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
k = 0;
|
||||||
|
pStorageEnd = storage_infos + storage_count;
|
||||||
|
for (pStorage=storage_infos; pStorage<pStorageEnd; \
|
||||||
|
pStorage++)
|
||||||
|
{
|
||||||
|
max_last_source_update = 0;
|
||||||
|
for (p=storage_infos; p<pStorageEnd; p++)
|
||||||
|
{
|
||||||
|
if (p != pStorage && p->stat.last_source_update
|
||||||
|
> max_last_source_update)
|
||||||
|
{
|
||||||
|
max_last_source_update = \
|
||||||
|
p->stat.last_source_update;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pStorageStat = &(pStorage->stat);
|
||||||
|
if (max_last_source_update == 0)
|
||||||
|
{
|
||||||
|
*szSyncedDelaySeconds = '\0';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (pStorageStat->last_synced_timestamp == 0)
|
||||||
|
{
|
||||||
|
strcpy(szSyncedDelaySeconds, "(never synced)");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int delay_seconds;
|
||||||
|
int remain_seconds;
|
||||||
|
int day;
|
||||||
|
int hour;
|
||||||
|
int minute;
|
||||||
|
int second;
|
||||||
|
char szDelayTime[64];
|
||||||
|
|
||||||
|
delay_seconds = (int)(max_last_source_update - \
|
||||||
|
pStorageStat->last_synced_timestamp);
|
||||||
|
day = delay_seconds / (24 * 3600);
|
||||||
|
remain_seconds = delay_seconds % (24 * 3600);
|
||||||
|
hour = remain_seconds / 3600;
|
||||||
|
remain_seconds %= 3600;
|
||||||
|
minute = remain_seconds / 60;
|
||||||
|
second = remain_seconds % 60;
|
||||||
|
|
||||||
|
if (day != 0)
|
||||||
|
{
|
||||||
|
sprintf(szDelayTime, "%d days " \
|
||||||
|
"%02dh:%02dm:%02ds", \
|
||||||
|
day, hour, minute, second);
|
||||||
|
}
|
||||||
|
else if (hour != 0)
|
||||||
|
{
|
||||||
|
sprintf(szDelayTime, "%02dh:%02dm:%02ds", \
|
||||||
|
hour, minute, second);
|
||||||
|
}
|
||||||
|
else if (minute != 0)
|
||||||
|
{
|
||||||
|
sprintf(szDelayTime, "%02dm:%02ds", minute, second);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sprintf(szDelayTime, "%ds", second);
|
||||||
|
}
|
||||||
|
|
||||||
|
sprintf(szSyncedDelaySeconds, "(%s delay)", szDelayTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getHostnameByIp(pStorage->ip_addr, szHostname, sizeof(szHostname));
|
||||||
|
if (*szHostname != '\0')
|
||||||
|
{
|
||||||
|
sprintf(szHostnamePrompt, " (%s)", szHostname);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*szHostnamePrompt = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pStorage->up_time != 0)
|
||||||
|
{
|
||||||
|
formatDatetime(pStorage->up_time, \
|
||||||
|
"%Y-%m-%d %H:%M:%S", \
|
||||||
|
szUpTime, sizeof(szUpTime));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*szUpTime = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
printf( "\tStorage %d:\n" \
|
||||||
|
"\t\tid = %s\n" \
|
||||||
|
"\t\tip_addr = %s%s %s\n" \
|
||||||
|
"\t\thttp domain = %s\n" \
|
||||||
|
"\t\tversion = %s\n" \
|
||||||
|
"\t\tjoin time = %s\n" \
|
||||||
|
"\t\tup time = %s\n" \
|
||||||
|
"\t\ttotal storage = %d MB\n" \
|
||||||
|
"\t\tfree storage = %d MB\n" \
|
||||||
|
"\t\tupload priority = %d\n" \
|
||||||
|
"\t\tstore_path_count = %d\n" \
|
||||||
|
"\t\tsubdir_count_per_path = %d\n" \
|
||||||
|
"\t\tstorage_port = %d\n" \
|
||||||
|
"\t\tstorage_http_port = %d\n" \
|
||||||
|
"\t\tcurrent_write_path = %d\n" \
|
||||||
|
"\t\tsource storage id= %s\n" \
|
||||||
|
"\t\tif_trunk_server= %d\n" \
|
||||||
|
"\t\ttotal_upload_count = "INT64_PRINTF_FORMAT"\n" \
|
||||||
|
"\t\tsuccess_upload_count = "INT64_PRINTF_FORMAT"\n" \
|
||||||
|
"\t\ttotal_append_count = "INT64_PRINTF_FORMAT"\n" \
|
||||||
|
"\t\tsuccess_append_count = "INT64_PRINTF_FORMAT"\n" \
|
||||||
|
"\t\ttotal_modify_count = "INT64_PRINTF_FORMAT"\n" \
|
||||||
|
"\t\tsuccess_modify_count = "INT64_PRINTF_FORMAT"\n" \
|
||||||
|
"\t\ttotal_truncate_count = "INT64_PRINTF_FORMAT"\n" \
|
||||||
|
"\t\tsuccess_truncate_count = "INT64_PRINTF_FORMAT"\n" \
|
||||||
|
"\t\ttotal_set_meta_count = "INT64_PRINTF_FORMAT"\n" \
|
||||||
|
"\t\tsuccess_set_meta_count = "INT64_PRINTF_FORMAT"\n" \
|
||||||
|
"\t\ttotal_delete_count = "INT64_PRINTF_FORMAT"\n" \
|
||||||
|
"\t\tsuccess_delete_count = "INT64_PRINTF_FORMAT"\n" \
|
||||||
|
"\t\ttotal_download_count = "INT64_PRINTF_FORMAT"\n" \
|
||||||
|
"\t\tsuccess_download_count = "INT64_PRINTF_FORMAT"\n" \
|
||||||
|
"\t\ttotal_get_meta_count = "INT64_PRINTF_FORMAT"\n" \
|
||||||
|
"\t\tsuccess_get_meta_count = "INT64_PRINTF_FORMAT"\n" \
|
||||||
|
"\t\ttotal_create_link_count = "INT64_PRINTF_FORMAT"\n" \
|
||||||
|
"\t\tsuccess_create_link_count = "INT64_PRINTF_FORMAT"\n"\
|
||||||
|
"\t\ttotal_delete_link_count = "INT64_PRINTF_FORMAT"\n" \
|
||||||
|
"\t\tsuccess_delete_link_count = "INT64_PRINTF_FORMAT"\n" \
|
||||||
|
"\t\ttotal_upload_bytes = "INT64_PRINTF_FORMAT"\n" \
|
||||||
|
"\t\tsuccess_upload_bytes = "INT64_PRINTF_FORMAT"\n" \
|
||||||
|
"\t\ttotal_append_bytes = "INT64_PRINTF_FORMAT"\n" \
|
||||||
|
"\t\tsuccess_append_bytes = "INT64_PRINTF_FORMAT"\n" \
|
||||||
|
"\t\ttotal_modify_bytes = "INT64_PRINTF_FORMAT"\n" \
|
||||||
|
"\t\tsuccess_modify_bytes = "INT64_PRINTF_FORMAT"\n" \
|
||||||
|
"\t\tstotal_download_bytes = "INT64_PRINTF_FORMAT"\n" \
|
||||||
|
"\t\tsuccess_download_bytes = "INT64_PRINTF_FORMAT"\n" \
|
||||||
|
"\t\ttotal_sync_in_bytes = "INT64_PRINTF_FORMAT"\n" \
|
||||||
|
"\t\tsuccess_sync_in_bytes = "INT64_PRINTF_FORMAT"\n" \
|
||||||
|
"\t\ttotal_sync_out_bytes = "INT64_PRINTF_FORMAT"\n" \
|
||||||
|
"\t\tsuccess_sync_out_bytes = "INT64_PRINTF_FORMAT"\n" \
|
||||||
|
"\t\ttotal_file_open_count = "INT64_PRINTF_FORMAT"\n" \
|
||||||
|
"\t\tsuccess_file_open_count = "INT64_PRINTF_FORMAT"\n" \
|
||||||
|
"\t\ttotal_file_read_count = "INT64_PRINTF_FORMAT"\n" \
|
||||||
|
"\t\tsuccess_file_read_count = "INT64_PRINTF_FORMAT"\n" \
|
||||||
|
"\t\ttotal_file_write_count = "INT64_PRINTF_FORMAT"\n" \
|
||||||
|
"\t\tsuccess_file_write_count = "INT64_PRINTF_FORMAT"\n" \
|
||||||
|
"\t\tlast_heart_beat_time = %s\n" \
|
||||||
|
"\t\tlast_source_update = %s\n" \
|
||||||
|
"\t\tlast_sync_update = %s\n" \
|
||||||
|
"\t\tlast_synced_timestamp = %s %s\n", \
|
||||||
|
++k, pStorage->id, pStorage->ip_addr, \
|
||||||
|
szHostnamePrompt, get_storage_status_caption( \
|
||||||
|
pStorage->status), pStorage->domain_name, \
|
||||||
|
pStorage->version, \
|
||||||
|
formatDatetime(pStorage->join_time, \
|
||||||
|
"%Y-%m-%d %H:%M:%S", \
|
||||||
|
szJoinTime, sizeof(szJoinTime)), \
|
||||||
|
szUpTime, pStorage->total_mb, \
|
||||||
|
pStorage->free_mb, \
|
||||||
|
pStorage->upload_priority, \
|
||||||
|
pStorage->store_path_count, \
|
||||||
|
pStorage->subdir_count_per_path, \
|
||||||
|
pStorage->storage_port, \
|
||||||
|
pStorage->storage_http_port, \
|
||||||
|
pStorage->current_write_path, \
|
||||||
|
pStorage->src_id, \
|
||||||
|
pStorage->if_trunk_server, \
|
||||||
|
pStorageStat->total_upload_count, \
|
||||||
|
pStorageStat->success_upload_count, \
|
||||||
|
pStorageStat->total_append_count, \
|
||||||
|
pStorageStat->success_append_count, \
|
||||||
|
pStorageStat->total_modify_count, \
|
||||||
|
pStorageStat->success_modify_count, \
|
||||||
|
pStorageStat->total_truncate_count, \
|
||||||
|
pStorageStat->success_truncate_count, \
|
||||||
|
pStorageStat->total_set_meta_count, \
|
||||||
|
pStorageStat->success_set_meta_count, \
|
||||||
|
pStorageStat->total_delete_count, \
|
||||||
|
pStorageStat->success_delete_count, \
|
||||||
|
pStorageStat->total_download_count, \
|
||||||
|
pStorageStat->success_download_count, \
|
||||||
|
pStorageStat->total_get_meta_count, \
|
||||||
|
pStorageStat->success_get_meta_count, \
|
||||||
|
pStorageStat->total_create_link_count, \
|
||||||
|
pStorageStat->success_create_link_count, \
|
||||||
|
pStorageStat->total_delete_link_count, \
|
||||||
|
pStorageStat->success_delete_link_count, \
|
||||||
|
pStorageStat->total_upload_bytes, \
|
||||||
|
pStorageStat->success_upload_bytes, \
|
||||||
|
pStorageStat->total_append_bytes, \
|
||||||
|
pStorageStat->success_append_bytes, \
|
||||||
|
pStorageStat->total_modify_bytes, \
|
||||||
|
pStorageStat->success_modify_bytes, \
|
||||||
|
pStorageStat->total_download_bytes, \
|
||||||
|
pStorageStat->success_download_bytes, \
|
||||||
|
pStorageStat->total_sync_in_bytes, \
|
||||||
|
pStorageStat->success_sync_in_bytes, \
|
||||||
|
pStorageStat->total_sync_out_bytes, \
|
||||||
|
pStorageStat->success_sync_out_bytes, \
|
||||||
|
pStorageStat->total_file_open_count, \
|
||||||
|
pStorageStat->success_file_open_count, \
|
||||||
|
pStorageStat->total_file_read_count, \
|
||||||
|
pStorageStat->success_file_read_count, \
|
||||||
|
pStorageStat->total_file_write_count, \
|
||||||
|
pStorageStat->success_file_write_count, \
|
||||||
|
formatDatetime(pStorageStat->last_heart_beat_time, \
|
||||||
|
"%Y-%m-%d %H:%M:%S", \
|
||||||
|
szLastHeartBeatTime, sizeof(szLastHeartBeatTime)), \
|
||||||
|
formatDatetime(pStorageStat->last_source_update, \
|
||||||
|
"%Y-%m-%d %H:%M:%S", \
|
||||||
|
szSrcUpdTime, sizeof(szSrcUpdTime)), \
|
||||||
|
formatDatetime(pStorageStat->last_sync_update, \
|
||||||
|
"%Y-%m-%d %H:%M:%S", \
|
||||||
|
szSyncUpdTime, sizeof(szSyncUpdTime)), \
|
||||||
|
formatDatetime(pStorageStat->last_synced_timestamp, \
|
||||||
|
"%Y-%m-%d %H:%M:%S", \
|
||||||
|
szSyncedTimestamp, sizeof(szSyncedTimestamp)),\
|
||||||
|
szSyncedDelaySeconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int list_all_groups(const char *group_name)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
int group_count;
|
||||||
|
FDFSGroupStat group_stats[FDFS_MAX_GROUPS];
|
||||||
|
FDFSGroupStat *pGroupStat;
|
||||||
|
FDFSGroupStat *pGroupEnd;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
result = tracker_list_groups(pTrackerServer, \
|
||||||
|
group_stats, FDFS_MAX_GROUPS, \
|
||||||
|
&group_count);
|
||||||
|
if (result != 0)
|
||||||
|
{
|
||||||
|
tracker_close_all_connections();
|
||||||
|
fdfs_client_destroy();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
pGroupEnd = group_stats + group_count;
|
||||||
|
if (group_name == NULL)
|
||||||
|
{
|
||||||
|
printf("group count: %d\n", group_count);
|
||||||
|
i = 0;
|
||||||
|
for (pGroupStat=group_stats; pGroupStat<pGroupEnd; \
|
||||||
|
pGroupStat++)
|
||||||
|
{
|
||||||
|
printf( "\nGroup %d:\n", ++i);
|
||||||
|
list_storages(pGroupStat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (pGroupStat=group_stats; pGroupStat<pGroupEnd; \
|
||||||
|
pGroupStat++)
|
||||||
|
{
|
||||||
|
if (strcmp(pGroupStat->group_name, group_name) == 0)
|
||||||
|
{
|
||||||
|
list_storages(pGroupStat);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,691 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2008 Happy Fish / YuQing
|
||||||
|
*
|
||||||
|
* FastDFS may be copied only under the terms of the GNU General
|
||||||
|
* Public License V3, which may be found in the FastDFS source kit.
|
||||||
|
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||||
|
**/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include "fdfs_client.h"
|
||||||
|
#include "fdfs_global.h"
|
||||||
|
#include "base64.h"
|
||||||
|
#include "sockopt.h"
|
||||||
|
#include "logger.h"
|
||||||
|
#include "fdfs_http_shared.h"
|
||||||
|
|
||||||
|
int writeToFileCallback(void *arg, const int64_t file_size, const char *data, \
|
||||||
|
const int current_size)
|
||||||
|
{
|
||||||
|
if (arg == NULL)
|
||||||
|
{
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fwrite(data, current_size, 1, (FILE *)arg) != 1)
|
||||||
|
{
|
||||||
|
return errno != 0 ? errno : EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int uploadFileCallback(void *arg, const int64_t file_size, int sock)
|
||||||
|
{
|
||||||
|
int64_t total_send_bytes;
|
||||||
|
char *filename;
|
||||||
|
|
||||||
|
if (arg == NULL)
|
||||||
|
{
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
filename = (char *)arg;
|
||||||
|
return tcpsendfile(sock, filename, file_size, \
|
||||||
|
g_fdfs_network_timeout, &total_send_bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
char *conf_filename;
|
||||||
|
char *local_filename;
|
||||||
|
ConnectionInfo *pTrackerServer;
|
||||||
|
ConnectionInfo *pStorageServer;
|
||||||
|
int result;
|
||||||
|
ConnectionInfo storageServer;
|
||||||
|
char group_name[FDFS_GROUP_NAME_MAX_LEN + 1];
|
||||||
|
char remote_filename[256];
|
||||||
|
char master_filename[256];
|
||||||
|
FDFSMetaData meta_list[32];
|
||||||
|
int meta_count;
|
||||||
|
int i;
|
||||||
|
FDFSMetaData *pMetaList;
|
||||||
|
char token[32 + 1];
|
||||||
|
char file_id[128];
|
||||||
|
char file_url[256];
|
||||||
|
char szDatetime[20];
|
||||||
|
char szPortPart[16];
|
||||||
|
int url_len;
|
||||||
|
time_t ts;
|
||||||
|
char *file_buff;
|
||||||
|
int64_t file_size;
|
||||||
|
char *operation;
|
||||||
|
char *meta_buff;
|
||||||
|
int store_path_index;
|
||||||
|
FDFSFileInfo file_info;
|
||||||
|
|
||||||
|
printf("This is FastDFS client test program v%d.%02d\n" \
|
||||||
|
"\nCopyright (C) 2008, Happy Fish / YuQing\n" \
|
||||||
|
"\nFastDFS may be copied only under the terms of the GNU General\n" \
|
||||||
|
"Public License V3, which may be found in the FastDFS source kit.\n" \
|
||||||
|
"Please visit the FastDFS Home Page http://www.csource.org/ \n" \
|
||||||
|
"for more detail.\n\n" \
|
||||||
|
, g_fdfs_version.major, g_fdfs_version.minor);
|
||||||
|
|
||||||
|
if (argc < 3)
|
||||||
|
{
|
||||||
|
printf("Usage: %s <config_file> <operation>\n" \
|
||||||
|
"\toperation: upload, download, getmeta, setmeta, " \
|
||||||
|
"delete and query_servers\n", argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_init();
|
||||||
|
g_log_context.log_level = LOG_DEBUG;
|
||||||
|
|
||||||
|
conf_filename = argv[1];
|
||||||
|
operation = argv[2];
|
||||||
|
if ((result=fdfs_client_init(conf_filename)) != 0)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
pTrackerServer = tracker_get_connection();
|
||||||
|
if (pTrackerServer == NULL)
|
||||||
|
{
|
||||||
|
fdfs_client_destroy();
|
||||||
|
return errno != 0 ? errno : ECONNREFUSED;
|
||||||
|
}
|
||||||
|
|
||||||
|
pStorageServer = NULL;
|
||||||
|
*group_name = '\0';
|
||||||
|
local_filename = NULL;
|
||||||
|
if (strcmp(operation, "upload") == 0)
|
||||||
|
{
|
||||||
|
int upload_type;
|
||||||
|
char *prefix_name;
|
||||||
|
const char *file_ext_name;
|
||||||
|
char slave_filename[256];
|
||||||
|
int slave_filename_len;
|
||||||
|
|
||||||
|
if (argc < 4)
|
||||||
|
{
|
||||||
|
printf("Usage: %s <config_file> upload " \
|
||||||
|
"<local_filename> [FILE | BUFF | CALLBACK] \n",\
|
||||||
|
argv[0]);
|
||||||
|
fdfs_client_destroy();
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
local_filename = argv[3];
|
||||||
|
if (argc == 4)
|
||||||
|
{
|
||||||
|
upload_type = FDFS_UPLOAD_BY_FILE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (strcmp(argv[4], "BUFF") == 0)
|
||||||
|
{
|
||||||
|
upload_type = FDFS_UPLOAD_BY_BUFF;
|
||||||
|
}
|
||||||
|
else if (strcmp(argv[4], "CALLBACK") == 0)
|
||||||
|
{
|
||||||
|
upload_type = FDFS_UPLOAD_BY_CALLBACK;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
upload_type = FDFS_UPLOAD_BY_FILE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
store_path_index = 0;
|
||||||
|
|
||||||
|
{
|
||||||
|
ConnectionInfo storageServers[FDFS_MAX_SERVERS_EACH_GROUP];
|
||||||
|
ConnectionInfo *pServer;
|
||||||
|
ConnectionInfo *pServerEnd;
|
||||||
|
int storage_count;
|
||||||
|
|
||||||
|
if ((result=tracker_query_storage_store_list_without_group( \
|
||||||
|
pTrackerServer, storageServers, \
|
||||||
|
FDFS_MAX_SERVERS_EACH_GROUP, &storage_count, \
|
||||||
|
group_name, &store_path_index)) == 0)
|
||||||
|
{
|
||||||
|
printf("tracker_query_storage_store_list_without_group: \n");
|
||||||
|
pServerEnd = storageServers + storage_count;
|
||||||
|
for (pServer=storageServers; pServer<pServerEnd; pServer++)
|
||||||
|
{
|
||||||
|
printf("\tserver %d. group_name=%s, " \
|
||||||
|
"ip_addr=%s, port=%d\n", \
|
||||||
|
(int)(pServer - storageServers) + 1, \
|
||||||
|
group_name, pServer->ip_addr, pServer->port);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((result=tracker_query_storage_store(pTrackerServer, \
|
||||||
|
&storageServer, group_name, &store_path_index)) != 0)
|
||||||
|
{
|
||||||
|
fdfs_client_destroy();
|
||||||
|
printf("tracker_query_storage fail, " \
|
||||||
|
"error no: %d, error info: %s\n", \
|
||||||
|
result, STRERROR(result));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("group_name=%s, ip_addr=%s, port=%d\n", \
|
||||||
|
group_name, storageServer.ip_addr, \
|
||||||
|
storageServer.port);
|
||||||
|
|
||||||
|
if ((pStorageServer=tracker_connect_server(&storageServer, \
|
||||||
|
&result)) == NULL)
|
||||||
|
{
|
||||||
|
fdfs_client_destroy();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&meta_list, 0, sizeof(meta_list));
|
||||||
|
meta_count = 0;
|
||||||
|
strcpy(meta_list[meta_count].name, "ext_name");
|
||||||
|
strcpy(meta_list[meta_count].value, "jpg");
|
||||||
|
meta_count++;
|
||||||
|
strcpy(meta_list[meta_count].name, "width");
|
||||||
|
strcpy(meta_list[meta_count].value, "160");
|
||||||
|
meta_count++;
|
||||||
|
strcpy(meta_list[meta_count].name, "height");
|
||||||
|
strcpy(meta_list[meta_count].value, "80");
|
||||||
|
meta_count++;
|
||||||
|
strcpy(meta_list[meta_count].name, "file_size");
|
||||||
|
strcpy(meta_list[meta_count].value, "115120");
|
||||||
|
meta_count++;
|
||||||
|
|
||||||
|
file_ext_name = fdfs_get_file_ext_name(local_filename);
|
||||||
|
*group_name = '\0';
|
||||||
|
|
||||||
|
if (upload_type == FDFS_UPLOAD_BY_FILE)
|
||||||
|
{
|
||||||
|
result = storage_upload_by_filename(pTrackerServer, \
|
||||||
|
pStorageServer, store_path_index, \
|
||||||
|
local_filename, file_ext_name, \
|
||||||
|
meta_list, meta_count, \
|
||||||
|
group_name, remote_filename);
|
||||||
|
|
||||||
|
printf("storage_upload_by_filename\n");
|
||||||
|
}
|
||||||
|
else if (upload_type == FDFS_UPLOAD_BY_BUFF)
|
||||||
|
{
|
||||||
|
char *file_content;
|
||||||
|
if ((result=getFileContent(local_filename, \
|
||||||
|
&file_content, &file_size)) == 0)
|
||||||
|
{
|
||||||
|
result = storage_upload_by_filebuff(pTrackerServer, \
|
||||||
|
pStorageServer, store_path_index, \
|
||||||
|
file_content, file_size, file_ext_name, \
|
||||||
|
meta_list, meta_count, \
|
||||||
|
group_name, remote_filename);
|
||||||
|
free(file_content);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("storage_upload_by_filebuff\n");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
struct stat stat_buf;
|
||||||
|
|
||||||
|
if (stat(local_filename, &stat_buf) == 0 && \
|
||||||
|
S_ISREG(stat_buf.st_mode))
|
||||||
|
{
|
||||||
|
file_size = stat_buf.st_size;
|
||||||
|
result = storage_upload_by_callback(pTrackerServer, \
|
||||||
|
pStorageServer, store_path_index, \
|
||||||
|
uploadFileCallback, local_filename, \
|
||||||
|
file_size, file_ext_name, \
|
||||||
|
meta_list, meta_count, \
|
||||||
|
group_name, remote_filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("storage_upload_by_callback\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result != 0)
|
||||||
|
{
|
||||||
|
printf("upload file fail, " \
|
||||||
|
"error no: %d, error info: %s\n", \
|
||||||
|
result, STRERROR(result));
|
||||||
|
tracker_disconnect_server_ex(pStorageServer, true);
|
||||||
|
fdfs_client_destroy();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_tracker_server_http_port == 80)
|
||||||
|
{
|
||||||
|
*szPortPart = '\0';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sprintf(szPortPart, ":%d", g_tracker_server_http_port);
|
||||||
|
}
|
||||||
|
|
||||||
|
sprintf(file_id, "%s/%s", group_name, remote_filename);
|
||||||
|
url_len = sprintf(file_url, "http://%s%s/%s", \
|
||||||
|
pStorageServer->ip_addr, szPortPart, file_id);
|
||||||
|
if (g_anti_steal_token)
|
||||||
|
{
|
||||||
|
ts = time(NULL);
|
||||||
|
fdfs_http_gen_token(&g_anti_steal_secret_key, file_id, \
|
||||||
|
ts, token);
|
||||||
|
sprintf(file_url + url_len, "?token=%s&ts=%d", \
|
||||||
|
token, (int)ts);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("group_name=%s, remote_filename=%s\n", \
|
||||||
|
group_name, remote_filename);
|
||||||
|
|
||||||
|
fdfs_get_file_info(group_name, remote_filename, &file_info);
|
||||||
|
printf("source ip address: %s\n", file_info.source_ip_addr);
|
||||||
|
printf("file timestamp=%s\n", formatDatetime(
|
||||||
|
file_info.create_timestamp, "%Y-%m-%d %H:%M:%S", \
|
||||||
|
szDatetime, sizeof(szDatetime)));
|
||||||
|
printf("file size="INT64_PRINTF_FORMAT"\n", file_info.file_size);
|
||||||
|
printf("file crc32=%u\n", file_info.crc32);
|
||||||
|
printf("example file url: %s\n", file_url);
|
||||||
|
|
||||||
|
strcpy(master_filename, remote_filename);
|
||||||
|
*remote_filename = '\0';
|
||||||
|
if (upload_type == FDFS_UPLOAD_BY_FILE)
|
||||||
|
{
|
||||||
|
prefix_name = "_big";
|
||||||
|
result = storage_upload_slave_by_filename(pTrackerServer,
|
||||||
|
NULL, local_filename, master_filename, \
|
||||||
|
prefix_name, file_ext_name, \
|
||||||
|
meta_list, meta_count, \
|
||||||
|
group_name, remote_filename);
|
||||||
|
|
||||||
|
printf("storage_upload_slave_by_filename\n");
|
||||||
|
}
|
||||||
|
else if (upload_type == FDFS_UPLOAD_BY_BUFF)
|
||||||
|
{
|
||||||
|
char *file_content;
|
||||||
|
prefix_name = "1024x1024";
|
||||||
|
if ((result=getFileContent(local_filename, \
|
||||||
|
&file_content, &file_size)) == 0)
|
||||||
|
{
|
||||||
|
result = storage_upload_slave_by_filebuff(pTrackerServer, \
|
||||||
|
NULL, file_content, file_size, master_filename,
|
||||||
|
prefix_name, file_ext_name, \
|
||||||
|
meta_list, meta_count, \
|
||||||
|
group_name, remote_filename);
|
||||||
|
free(file_content);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("storage_upload_slave_by_filebuff\n");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
struct stat stat_buf;
|
||||||
|
|
||||||
|
prefix_name = "-small";
|
||||||
|
if (stat(local_filename, &stat_buf) == 0 && \
|
||||||
|
S_ISREG(stat_buf.st_mode))
|
||||||
|
{
|
||||||
|
file_size = stat_buf.st_size;
|
||||||
|
result = storage_upload_slave_by_callback(pTrackerServer, \
|
||||||
|
NULL, uploadFileCallback, local_filename, \
|
||||||
|
file_size, master_filename, prefix_name, \
|
||||||
|
file_ext_name, meta_list, meta_count, \
|
||||||
|
group_name, remote_filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("storage_upload_slave_by_callback\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result != 0)
|
||||||
|
{
|
||||||
|
printf("upload slave file fail, " \
|
||||||
|
"error no: %d, error info: %s\n", \
|
||||||
|
result, STRERROR(result));
|
||||||
|
tracker_disconnect_server_ex(pStorageServer, true);
|
||||||
|
fdfs_client_destroy();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_tracker_server_http_port == 80)
|
||||||
|
{
|
||||||
|
*szPortPart = '\0';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sprintf(szPortPart, ":%d", g_tracker_server_http_port);
|
||||||
|
}
|
||||||
|
|
||||||
|
sprintf(file_id, "%s/%s", group_name, remote_filename);
|
||||||
|
url_len = sprintf(file_url, "http://%s%s/%s", \
|
||||||
|
pStorageServer->ip_addr, szPortPart, file_id);
|
||||||
|
if (g_anti_steal_token)
|
||||||
|
{
|
||||||
|
ts = time(NULL);
|
||||||
|
fdfs_http_gen_token(&g_anti_steal_secret_key, file_id, \
|
||||||
|
ts, token);
|
||||||
|
sprintf(file_url + url_len, "?token=%s&ts=%d", \
|
||||||
|
token, (int)ts);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("group_name=%s, remote_filename=%s\n", \
|
||||||
|
group_name, remote_filename);
|
||||||
|
|
||||||
|
fdfs_get_file_info(group_name, remote_filename, &file_info);
|
||||||
|
|
||||||
|
printf("source ip address: %s\n", file_info.source_ip_addr);
|
||||||
|
printf("file timestamp=%s\n", formatDatetime(
|
||||||
|
file_info.create_timestamp, "%Y-%m-%d %H:%M:%S", \
|
||||||
|
szDatetime, sizeof(szDatetime)));
|
||||||
|
printf("file size="INT64_PRINTF_FORMAT"\n", file_info.file_size);
|
||||||
|
printf("file crc32=%u\n", file_info.crc32);
|
||||||
|
printf("example file url: %s\n", file_url);
|
||||||
|
|
||||||
|
if (fdfs_gen_slave_filename(master_filename, \
|
||||||
|
prefix_name, file_ext_name, \
|
||||||
|
slave_filename, &slave_filename_len) == 0)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (strcmp(remote_filename, slave_filename) != 0)
|
||||||
|
{
|
||||||
|
printf("slave_filename=%s\n" \
|
||||||
|
"remote_filename=%s\n" \
|
||||||
|
"not equal!\n", \
|
||||||
|
slave_filename, remote_filename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (strcmp(operation, "download") == 0 ||
|
||||||
|
strcmp(operation, "getmeta") == 0 ||
|
||||||
|
strcmp(operation, "setmeta") == 0 ||
|
||||||
|
strcmp(operation, "query_servers") == 0 ||
|
||||||
|
strcmp(operation, "delete") == 0)
|
||||||
|
{
|
||||||
|
if (argc < 5)
|
||||||
|
{
|
||||||
|
printf("Usage: %s <config_file> %s " \
|
||||||
|
"<group_name> <remote_filename>\n", \
|
||||||
|
argv[0], operation);
|
||||||
|
fdfs_client_destroy();
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(group_name, sizeof(group_name), "%s", argv[3]);
|
||||||
|
snprintf(remote_filename, sizeof(remote_filename), \
|
||||||
|
"%s", argv[4]);
|
||||||
|
if (strcmp(operation, "setmeta") == 0 ||
|
||||||
|
strcmp(operation, "delete") == 0)
|
||||||
|
{
|
||||||
|
result = tracker_query_storage_update(pTrackerServer, \
|
||||||
|
&storageServer, group_name, remote_filename);
|
||||||
|
}
|
||||||
|
else if (strcmp(operation, "query_servers") == 0)
|
||||||
|
{
|
||||||
|
ConnectionInfo storageServers[FDFS_MAX_SERVERS_EACH_GROUP];
|
||||||
|
int server_count;
|
||||||
|
|
||||||
|
result = tracker_query_storage_list(pTrackerServer, \
|
||||||
|
storageServers, FDFS_MAX_SERVERS_EACH_GROUP, \
|
||||||
|
&server_count, group_name, remote_filename);
|
||||||
|
|
||||||
|
if (result != 0)
|
||||||
|
{
|
||||||
|
printf("tracker_query_storage_list fail, "\
|
||||||
|
"group_name=%s, filename=%s, " \
|
||||||
|
"error no: %d, error info: %s\n", \
|
||||||
|
group_name, remote_filename, \
|
||||||
|
result, STRERROR(result));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("server list (%d):\n", server_count);
|
||||||
|
for (i=0; i<server_count; i++)
|
||||||
|
{
|
||||||
|
printf("\t%s:%d\n", \
|
||||||
|
storageServers[i].ip_addr, \
|
||||||
|
storageServers[i].port);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
tracker_disconnect_server_ex(pTrackerServer, result != 0);
|
||||||
|
fdfs_client_destroy();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = tracker_query_storage_fetch(pTrackerServer, \
|
||||||
|
&storageServer, group_name, remote_filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result != 0)
|
||||||
|
{
|
||||||
|
fdfs_client_destroy();
|
||||||
|
printf("tracker_query_storage_fetch fail, " \
|
||||||
|
"group_name=%s, filename=%s, " \
|
||||||
|
"error no: %d, error info: %s\n", \
|
||||||
|
group_name, remote_filename, \
|
||||||
|
result, STRERROR(result));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("storage=%s:%d\n", storageServer.ip_addr, \
|
||||||
|
storageServer.port);
|
||||||
|
|
||||||
|
if ((pStorageServer=tracker_connect_server(&storageServer, \
|
||||||
|
&result)) == NULL)
|
||||||
|
{
|
||||||
|
fdfs_client_destroy();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(operation, "download") == 0)
|
||||||
|
{
|
||||||
|
if (argc >= 6)
|
||||||
|
{
|
||||||
|
local_filename = argv[5];
|
||||||
|
if (strcmp(local_filename, "CALLBACK") == 0)
|
||||||
|
{
|
||||||
|
FILE *fp;
|
||||||
|
fp = fopen(local_filename, "wb");
|
||||||
|
if (fp == NULL)
|
||||||
|
{
|
||||||
|
result = errno != 0 ? errno : EPERM;
|
||||||
|
printf("open file \"%s\" fail, " \
|
||||||
|
"errno: %d, error info: %s", \
|
||||||
|
local_filename, result, \
|
||||||
|
STRERROR(result));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = storage_download_file_ex( \
|
||||||
|
pTrackerServer, pStorageServer, \
|
||||||
|
group_name, remote_filename, 0, 0, \
|
||||||
|
writeToFileCallback, fp, &file_size);
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = storage_download_file_to_file( \
|
||||||
|
pTrackerServer, pStorageServer, \
|
||||||
|
group_name, remote_filename, \
|
||||||
|
local_filename, &file_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
file_buff = NULL;
|
||||||
|
if ((result=storage_download_file_to_buff( \
|
||||||
|
pTrackerServer, pStorageServer, \
|
||||||
|
group_name, remote_filename, \
|
||||||
|
&file_buff, &file_size)) == 0)
|
||||||
|
{
|
||||||
|
local_filename = strrchr( \
|
||||||
|
remote_filename, '/');
|
||||||
|
if (local_filename != NULL)
|
||||||
|
{
|
||||||
|
local_filename++; //skip /
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
local_filename=remote_filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = writeToFile(local_filename, \
|
||||||
|
file_buff, file_size);
|
||||||
|
|
||||||
|
free(file_buff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result == 0)
|
||||||
|
{
|
||||||
|
printf("download file success, " \
|
||||||
|
"file size="INT64_PRINTF_FORMAT", file save to %s\n", \
|
||||||
|
file_size, local_filename);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("download file fail, " \
|
||||||
|
"error no: %d, error info: %s\n", \
|
||||||
|
result, STRERROR(result));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (strcmp(operation, "getmeta") == 0)
|
||||||
|
{
|
||||||
|
if ((result=storage_get_metadata(pTrackerServer, \
|
||||||
|
pStorageServer, group_name, remote_filename, \
|
||||||
|
&pMetaList, &meta_count)) == 0)
|
||||||
|
{
|
||||||
|
printf("get meta data success, " \
|
||||||
|
"meta count=%d\n", meta_count);
|
||||||
|
for (i=0; i<meta_count; i++)
|
||||||
|
{
|
||||||
|
printf("%s=%s\n", \
|
||||||
|
pMetaList[i].name, \
|
||||||
|
pMetaList[i].value);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(pMetaList);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("getmeta fail, " \
|
||||||
|
"error no: %d, error info: %s\n", \
|
||||||
|
result, STRERROR(result));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (strcmp(operation, "setmeta") == 0)
|
||||||
|
{
|
||||||
|
if (argc < 7)
|
||||||
|
{
|
||||||
|
printf("Usage: %s <config_file> %s " \
|
||||||
|
"<group_name> <remote_filename> " \
|
||||||
|
"<op_flag> <metadata_list>\n" \
|
||||||
|
"\top_flag: %c for overwrite, " \
|
||||||
|
"%c for merge\n" \
|
||||||
|
"\tmetadata_list: name1=value1," \
|
||||||
|
"name2=value2,...\n", \
|
||||||
|
argv[0], operation, \
|
||||||
|
STORAGE_SET_METADATA_FLAG_OVERWRITE, \
|
||||||
|
STORAGE_SET_METADATA_FLAG_MERGE);
|
||||||
|
fdfs_client_destroy();
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
meta_buff = strdup(argv[6]);
|
||||||
|
if (meta_buff == NULL)
|
||||||
|
{
|
||||||
|
printf("Out of memory!\n");
|
||||||
|
return ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
pMetaList = fdfs_split_metadata_ex(meta_buff, \
|
||||||
|
',', '=', &meta_count, &result);
|
||||||
|
if (pMetaList == NULL)
|
||||||
|
{
|
||||||
|
printf("Out of memory!\n");
|
||||||
|
free(meta_buff);
|
||||||
|
return ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((result=storage_set_metadata(pTrackerServer, \
|
||||||
|
NULL, group_name, remote_filename, \
|
||||||
|
pMetaList, meta_count, *argv[5])) == 0)
|
||||||
|
{
|
||||||
|
printf("set meta data success\n");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("setmeta fail, " \
|
||||||
|
"error no: %d, error info: %s\n", \
|
||||||
|
result, STRERROR(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
free(meta_buff);
|
||||||
|
free(pMetaList);
|
||||||
|
}
|
||||||
|
else if(strcmp(operation, "delete") == 0)
|
||||||
|
{
|
||||||
|
if ((result=storage_delete_file(pTrackerServer, \
|
||||||
|
NULL, group_name, remote_filename)) == 0)
|
||||||
|
{
|
||||||
|
printf("delete file success\n");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("delete file fail, " \
|
||||||
|
"error no: %d, error info: %s\n", \
|
||||||
|
result, STRERROR(result));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fdfs_client_destroy();
|
||||||
|
printf("invalid operation: %s\n", operation);
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* for test only */
|
||||||
|
if ((result=fdfs_active_test(pTrackerServer)) != 0)
|
||||||
|
{
|
||||||
|
printf("active_test to tracker server %s:%d fail, errno: %d\n", \
|
||||||
|
pTrackerServer->ip_addr, pTrackerServer->port, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* for test only */
|
||||||
|
if ((result=fdfs_active_test(pStorageServer)) != 0)
|
||||||
|
{
|
||||||
|
printf("active_test to storage server %s:%d fail, errno: %d\n", \
|
||||||
|
pStorageServer->ip_addr, pStorageServer->port, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
tracker_disconnect_server_ex(pStorageServer, true);
|
||||||
|
tracker_disconnect_server_ex(pTrackerServer, true);
|
||||||
|
|
||||||
|
fdfs_client_destroy();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,658 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2008 Happy Fish / YuQing
|
||||||
|
*
|
||||||
|
* FastDFS may be copied only under the terms of the GNU General
|
||||||
|
* Public License V3, which may be found in the FastDFS source kit.
|
||||||
|
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||||
|
**/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include "fdfs_client.h"
|
||||||
|
#include "fdfs_global.h"
|
||||||
|
#include "base64.h"
|
||||||
|
#include "fdfs_http_shared.h"
|
||||||
|
#include "sockopt.h"
|
||||||
|
#include "logger.h"
|
||||||
|
|
||||||
|
int writeToFileCallback(void *arg, const int64_t file_size, const char *data, \
|
||||||
|
const int current_size)
|
||||||
|
{
|
||||||
|
if (arg == NULL)
|
||||||
|
{
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fwrite(data, current_size, 1, (FILE *)arg) != 1)
|
||||||
|
{
|
||||||
|
return errno != 0 ? errno : EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int uploadFileCallback(void *arg, const int64_t file_size, int sock)
|
||||||
|
{
|
||||||
|
int64_t total_send_bytes;
|
||||||
|
char *filename;
|
||||||
|
if (arg == NULL)
|
||||||
|
{
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
filename = (char *)arg;
|
||||||
|
return tcpsendfile(sock, filename, file_size, \
|
||||||
|
g_fdfs_network_timeout, &total_send_bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
char *conf_filename;
|
||||||
|
char *local_filename;
|
||||||
|
ConnectionInfo *pTrackerServer;
|
||||||
|
ConnectionInfo *pStorageServer;
|
||||||
|
int result;
|
||||||
|
ConnectionInfo storageServer;
|
||||||
|
char group_name[FDFS_GROUP_NAME_MAX_LEN + 1];
|
||||||
|
FDFSMetaData meta_list[32];
|
||||||
|
int meta_count;
|
||||||
|
int i;
|
||||||
|
FDFSMetaData *pMetaList;
|
||||||
|
char token[32 + 1];
|
||||||
|
char file_id[128];
|
||||||
|
char master_file_id[128];
|
||||||
|
char file_url[256];
|
||||||
|
char szDatetime[20];
|
||||||
|
char szPortPart[16];
|
||||||
|
int url_len;
|
||||||
|
time_t ts;
|
||||||
|
char *file_buff;
|
||||||
|
int64_t file_size;
|
||||||
|
char *operation;
|
||||||
|
char *meta_buff;
|
||||||
|
int store_path_index;
|
||||||
|
FDFSFileInfo file_info;
|
||||||
|
|
||||||
|
printf("This is FastDFS client test program v%d.%02d\n" \
|
||||||
|
"\nCopyright (C) 2008, Happy Fish / YuQing\n" \
|
||||||
|
"\nFastDFS may be copied only under the terms of the GNU General\n" \
|
||||||
|
"Public License V3, which may be found in the FastDFS source kit.\n" \
|
||||||
|
"Please visit the FastDFS Home Page http://www.csource.org/ \n" \
|
||||||
|
"for more detail.\n\n" \
|
||||||
|
, g_fdfs_version.major, g_fdfs_version.minor);
|
||||||
|
|
||||||
|
if (argc < 3)
|
||||||
|
{
|
||||||
|
printf("Usage: %s <config_file> <operation>\n" \
|
||||||
|
"\toperation: upload, download, getmeta, setmeta, " \
|
||||||
|
"delete and query_servers\n", argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_init();
|
||||||
|
g_log_context.log_level = LOG_DEBUG;
|
||||||
|
|
||||||
|
conf_filename = argv[1];
|
||||||
|
operation = argv[2];
|
||||||
|
if ((result=fdfs_client_init(conf_filename)) != 0)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
pTrackerServer = tracker_get_connection();
|
||||||
|
if (pTrackerServer == NULL)
|
||||||
|
{
|
||||||
|
fdfs_client_destroy();
|
||||||
|
return errno != 0 ? errno : ECONNREFUSED;
|
||||||
|
}
|
||||||
|
|
||||||
|
local_filename = NULL;
|
||||||
|
if (strcmp(operation, "upload") == 0)
|
||||||
|
{
|
||||||
|
int upload_type;
|
||||||
|
char *prefix_name;
|
||||||
|
const char *file_ext_name;
|
||||||
|
char slave_file_id[256];
|
||||||
|
int slave_file_id_len;
|
||||||
|
|
||||||
|
if (argc < 4)
|
||||||
|
{
|
||||||
|
printf("Usage: %s <config_file> upload " \
|
||||||
|
"<local_filename> [FILE | BUFF | CALLBACK] \n",\
|
||||||
|
argv[0]);
|
||||||
|
fdfs_client_destroy();
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
local_filename = argv[3];
|
||||||
|
if (argc == 4)
|
||||||
|
{
|
||||||
|
upload_type = FDFS_UPLOAD_BY_FILE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (strcmp(argv[4], "BUFF") == 0)
|
||||||
|
{
|
||||||
|
upload_type = FDFS_UPLOAD_BY_BUFF;
|
||||||
|
}
|
||||||
|
else if (strcmp(argv[4], "CALLBACK") == 0)
|
||||||
|
{
|
||||||
|
upload_type = FDFS_UPLOAD_BY_CALLBACK;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
upload_type = FDFS_UPLOAD_BY_FILE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
ConnectionInfo storageServers[FDFS_MAX_SERVERS_EACH_GROUP];
|
||||||
|
ConnectionInfo *pServer;
|
||||||
|
ConnectionInfo *pServerEnd;
|
||||||
|
int storage_count;
|
||||||
|
|
||||||
|
strcpy(group_name, "group1");
|
||||||
|
if ((result=tracker_query_storage_store_list_with_group( \
|
||||||
|
pTrackerServer, group_name, storageServers, \
|
||||||
|
FDFS_MAX_SERVERS_EACH_GROUP, &storage_count, \
|
||||||
|
&store_path_index)) == 0)
|
||||||
|
{
|
||||||
|
printf("tracker_query_storage_store_list_with_group: \n");
|
||||||
|
pServerEnd = storageServers + storage_count;
|
||||||
|
for (pServer=storageServers; pServer<pServerEnd; pServer++)
|
||||||
|
{
|
||||||
|
printf("\tserver %d. group_name=%s, " \
|
||||||
|
"ip_addr=%s, port=%d\n", \
|
||||||
|
(int)(pServer - storageServers) + 1, \
|
||||||
|
group_name, pServer->ip_addr, \
|
||||||
|
pServer->port);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*group_name = '\0';
|
||||||
|
if ((result=tracker_query_storage_store(pTrackerServer, \
|
||||||
|
&storageServer, group_name, &store_path_index)) != 0)
|
||||||
|
{
|
||||||
|
fdfs_client_destroy();
|
||||||
|
printf("tracker_query_storage fail, " \
|
||||||
|
"error no: %d, error info: %s\n", \
|
||||||
|
result, STRERROR(result));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("group_name=%s, ip_addr=%s, port=%d\n", \
|
||||||
|
group_name, storageServer.ip_addr, \
|
||||||
|
storageServer.port);
|
||||||
|
|
||||||
|
if ((pStorageServer=tracker_connect_server(&storageServer, \
|
||||||
|
&result)) == NULL)
|
||||||
|
{
|
||||||
|
fdfs_client_destroy();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&meta_list, 0, sizeof(meta_list));
|
||||||
|
meta_count = 0;
|
||||||
|
strcpy(meta_list[meta_count].name, "ext_name");
|
||||||
|
strcpy(meta_list[meta_count].value, "jpg");
|
||||||
|
meta_count++;
|
||||||
|
strcpy(meta_list[meta_count].name, "width");
|
||||||
|
strcpy(meta_list[meta_count].value, "160");
|
||||||
|
meta_count++;
|
||||||
|
strcpy(meta_list[meta_count].name, "height");
|
||||||
|
strcpy(meta_list[meta_count].value, "80");
|
||||||
|
meta_count++;
|
||||||
|
strcpy(meta_list[meta_count].name, "file_size");
|
||||||
|
strcpy(meta_list[meta_count].value, "115120");
|
||||||
|
meta_count++;
|
||||||
|
|
||||||
|
file_ext_name = fdfs_get_file_ext_name(local_filename);
|
||||||
|
strcpy(group_name, "");
|
||||||
|
|
||||||
|
if (upload_type == FDFS_UPLOAD_BY_FILE)
|
||||||
|
{
|
||||||
|
printf("storage_upload_by_filename\n");
|
||||||
|
result = storage_upload_by_filename1(pTrackerServer, \
|
||||||
|
pStorageServer, store_path_index, \
|
||||||
|
local_filename, file_ext_name, \
|
||||||
|
meta_list, meta_count, \
|
||||||
|
group_name, file_id);
|
||||||
|
}
|
||||||
|
else if (upload_type == FDFS_UPLOAD_BY_BUFF)
|
||||||
|
{
|
||||||
|
char *file_content;
|
||||||
|
printf("storage_upload_by_filebuff\n");
|
||||||
|
if ((result=getFileContent(local_filename, \
|
||||||
|
&file_content, &file_size)) == 0)
|
||||||
|
{
|
||||||
|
result = storage_upload_by_filebuff1(pTrackerServer, \
|
||||||
|
pStorageServer, store_path_index, \
|
||||||
|
file_content, file_size, file_ext_name, \
|
||||||
|
meta_list, meta_count, \
|
||||||
|
group_name, file_id);
|
||||||
|
free(file_content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
struct stat stat_buf;
|
||||||
|
|
||||||
|
printf("storage_upload_by_callback\n");
|
||||||
|
if (stat(local_filename, &stat_buf) == 0 && \
|
||||||
|
S_ISREG(stat_buf.st_mode))
|
||||||
|
{
|
||||||
|
file_size = stat_buf.st_size;
|
||||||
|
result = storage_upload_by_callback1(pTrackerServer, \
|
||||||
|
pStorageServer, store_path_index, \
|
||||||
|
uploadFileCallback, local_filename, \
|
||||||
|
file_size, file_ext_name, \
|
||||||
|
meta_list, meta_count, \
|
||||||
|
group_name, file_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result != 0)
|
||||||
|
{
|
||||||
|
printf("upload file fail, " \
|
||||||
|
"error no: %d, error info: %s\n", \
|
||||||
|
result, STRERROR(result));
|
||||||
|
tracker_disconnect_server_ex(pStorageServer, true);
|
||||||
|
fdfs_client_destroy();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_tracker_server_http_port == 80)
|
||||||
|
{
|
||||||
|
*szPortPart = '\0';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sprintf(szPortPart, ":%d", g_tracker_server_http_port);
|
||||||
|
}
|
||||||
|
|
||||||
|
url_len = sprintf(file_url, "http://%s%s/%s", \
|
||||||
|
pStorageServer->ip_addr, szPortPart, file_id);
|
||||||
|
if (g_anti_steal_token)
|
||||||
|
{
|
||||||
|
ts = time(NULL);
|
||||||
|
fdfs_http_gen_token(&g_anti_steal_secret_key, \
|
||||||
|
file_id, ts, token);
|
||||||
|
sprintf(file_url + url_len, "?token=%s&ts=%d", \
|
||||||
|
token, (int)ts);
|
||||||
|
}
|
||||||
|
|
||||||
|
fdfs_get_file_info1(file_id, &file_info);
|
||||||
|
printf("source ip address: %s\n", file_info.source_ip_addr);
|
||||||
|
printf("file timestamp=%s\n", formatDatetime(
|
||||||
|
file_info.create_timestamp, "%Y-%m-%d %H:%M:%S", \
|
||||||
|
szDatetime, sizeof(szDatetime)));
|
||||||
|
printf("file size="INT64_PRINTF_FORMAT"\n", file_info.file_size);
|
||||||
|
printf("file crc32=%u\n", file_info.crc32);
|
||||||
|
printf("example file url: %s\n", file_url);
|
||||||
|
|
||||||
|
strcpy(master_file_id, file_id);
|
||||||
|
*file_id = '\0';
|
||||||
|
|
||||||
|
if (upload_type == FDFS_UPLOAD_BY_FILE)
|
||||||
|
{
|
||||||
|
prefix_name = "_big";
|
||||||
|
printf("storage_upload_slave_by_filename\n");
|
||||||
|
result = storage_upload_slave_by_filename1( \
|
||||||
|
pTrackerServer, NULL, \
|
||||||
|
local_filename, master_file_id, \
|
||||||
|
prefix_name, file_ext_name, \
|
||||||
|
meta_list, meta_count, file_id);
|
||||||
|
}
|
||||||
|
else if (upload_type == FDFS_UPLOAD_BY_BUFF)
|
||||||
|
{
|
||||||
|
char *file_content;
|
||||||
|
prefix_name = "1024x1024";
|
||||||
|
printf("storage_upload_slave_by_filebuff\n");
|
||||||
|
if ((result=getFileContent(local_filename, \
|
||||||
|
&file_content, &file_size)) == 0)
|
||||||
|
{
|
||||||
|
result = storage_upload_slave_by_filebuff1( \
|
||||||
|
pTrackerServer, NULL, file_content, file_size, \
|
||||||
|
master_file_id, prefix_name, file_ext_name, \
|
||||||
|
meta_list, meta_count, file_id);
|
||||||
|
free(file_content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
struct stat stat_buf;
|
||||||
|
|
||||||
|
prefix_name = "_small";
|
||||||
|
printf("storage_upload_slave_by_callback\n");
|
||||||
|
if (stat(local_filename, &stat_buf) == 0 && \
|
||||||
|
S_ISREG(stat_buf.st_mode))
|
||||||
|
{
|
||||||
|
file_size = stat_buf.st_size;
|
||||||
|
result = storage_upload_slave_by_callback1( \
|
||||||
|
pTrackerServer, NULL, \
|
||||||
|
uploadFileCallback, local_filename, \
|
||||||
|
file_size, master_file_id, \
|
||||||
|
prefix_name, file_ext_name, \
|
||||||
|
meta_list, meta_count, file_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result != 0)
|
||||||
|
{
|
||||||
|
printf("upload slave file fail, " \
|
||||||
|
"error no: %d, error info: %s\n", \
|
||||||
|
result, STRERROR(result));
|
||||||
|
tracker_disconnect_server_ex(pStorageServer, true);
|
||||||
|
fdfs_client_destroy();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_tracker_server_http_port == 80)
|
||||||
|
{
|
||||||
|
*szPortPart = '\0';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sprintf(szPortPart, ":%d", g_tracker_server_http_port);
|
||||||
|
}
|
||||||
|
url_len = sprintf(file_url, "http://%s%s/%s", \
|
||||||
|
pStorageServer->ip_addr, szPortPart, file_id);
|
||||||
|
if (g_anti_steal_token)
|
||||||
|
{
|
||||||
|
ts = time(NULL);
|
||||||
|
fdfs_http_gen_token(&g_anti_steal_secret_key, \
|
||||||
|
file_id, ts, token);
|
||||||
|
sprintf(file_url + url_len, "?token=%s&ts=%d", \
|
||||||
|
token, (int)ts);
|
||||||
|
}
|
||||||
|
|
||||||
|
fdfs_get_file_info1(file_id, &file_info);
|
||||||
|
printf("source ip address: %s\n", file_info.source_ip_addr);
|
||||||
|
printf("file timestamp=%s\n", formatDatetime(
|
||||||
|
file_info.create_timestamp, "%Y-%m-%d %H:%M:%S", \
|
||||||
|
szDatetime, sizeof(szDatetime)));
|
||||||
|
printf("file size="INT64_PRINTF_FORMAT"\n", file_info.file_size);
|
||||||
|
printf("file crc32=%u\n", file_info.crc32);
|
||||||
|
printf("example file url: %s\n", file_url);
|
||||||
|
|
||||||
|
if (fdfs_gen_slave_filename(master_file_id, \
|
||||||
|
prefix_name, file_ext_name, \
|
||||||
|
slave_file_id, &slave_file_id_len) == 0)
|
||||||
|
{
|
||||||
|
if (strcmp(file_id, slave_file_id) != 0)
|
||||||
|
{
|
||||||
|
printf("slave_file_id=%s\n" \
|
||||||
|
"file_id=%s\n" \
|
||||||
|
"not equal!\n", \
|
||||||
|
slave_file_id, file_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (strcmp(operation, "download") == 0 ||
|
||||||
|
strcmp(operation, "getmeta") == 0 ||
|
||||||
|
strcmp(operation, "setmeta") == 0 ||
|
||||||
|
strcmp(operation, "query_servers") == 0 ||
|
||||||
|
strcmp(operation, "delete") == 0)
|
||||||
|
{
|
||||||
|
if (argc < 4)
|
||||||
|
{
|
||||||
|
printf("Usage: %s <config_file> %s " \
|
||||||
|
"<file_id>\n", \
|
||||||
|
argv[0], operation);
|
||||||
|
fdfs_client_destroy();
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(file_id, sizeof(file_id), "%s", argv[3]);
|
||||||
|
if (strcmp(operation, "query_servers") == 0)
|
||||||
|
{
|
||||||
|
ConnectionInfo storageServers[FDFS_MAX_SERVERS_EACH_GROUP];
|
||||||
|
int server_count;
|
||||||
|
|
||||||
|
result = tracker_query_storage_list1(pTrackerServer, \
|
||||||
|
storageServers, FDFS_MAX_SERVERS_EACH_GROUP, \
|
||||||
|
&server_count, file_id);
|
||||||
|
|
||||||
|
if (result != 0)
|
||||||
|
{
|
||||||
|
printf("tracker_query_storage_list1 fail, "\
|
||||||
|
"file_id=%s, " \
|
||||||
|
"error no: %d, error info: %s\n", \
|
||||||
|
file_id, result, STRERROR(result));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("server list (%d):\n", server_count);
|
||||||
|
for (i=0; i<server_count; i++)
|
||||||
|
{
|
||||||
|
printf("\t%s:%d\n", \
|
||||||
|
storageServers[i].ip_addr, \
|
||||||
|
storageServers[i].port);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
tracker_disconnect_server_ex(pTrackerServer, true);
|
||||||
|
fdfs_client_destroy();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((result=tracker_query_storage_fetch1(pTrackerServer, \
|
||||||
|
&storageServer, file_id)) != 0)
|
||||||
|
{
|
||||||
|
fdfs_client_destroy();
|
||||||
|
printf("tracker_query_storage_fetch fail, " \
|
||||||
|
"file_id=%s, " \
|
||||||
|
"error no: %d, error info: %s\n", \
|
||||||
|
file_id, result, STRERROR(result));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("storage=%s:%d\n", storageServer.ip_addr, \
|
||||||
|
storageServer.port);
|
||||||
|
|
||||||
|
if ((pStorageServer=tracker_connect_server(&storageServer, \
|
||||||
|
&result)) == NULL)
|
||||||
|
{
|
||||||
|
fdfs_client_destroy();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(operation, "download") == 0)
|
||||||
|
{
|
||||||
|
if (argc >= 5)
|
||||||
|
{
|
||||||
|
local_filename = argv[4];
|
||||||
|
if (strcmp(local_filename, "CALLBACK") == 0)
|
||||||
|
{
|
||||||
|
FILE *fp;
|
||||||
|
fp = fopen(local_filename, "wb");
|
||||||
|
if (fp == NULL)
|
||||||
|
{
|
||||||
|
result = errno != 0 ? errno : EPERM;
|
||||||
|
printf("open file \"%s\" fail, " \
|
||||||
|
"errno: %d, error info: %s", \
|
||||||
|
local_filename, result, \
|
||||||
|
STRERROR(result));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = storage_download_file_ex1( \
|
||||||
|
pTrackerServer, pStorageServer, \
|
||||||
|
file_id, 0, 0, \
|
||||||
|
writeToFileCallback, fp, &file_size);
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = storage_download_file_to_file1( \
|
||||||
|
pTrackerServer, pStorageServer, \
|
||||||
|
file_id, \
|
||||||
|
local_filename, &file_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
file_buff = NULL;
|
||||||
|
if ((result=storage_download_file_to_buff1( \
|
||||||
|
pTrackerServer, pStorageServer, \
|
||||||
|
file_id, \
|
||||||
|
&file_buff, &file_size)) == 0)
|
||||||
|
{
|
||||||
|
local_filename = strrchr( \
|
||||||
|
file_id, '/');
|
||||||
|
if (local_filename != NULL)
|
||||||
|
{
|
||||||
|
local_filename++; //skip /
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
local_filename=file_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = writeToFile(local_filename, \
|
||||||
|
file_buff, file_size);
|
||||||
|
|
||||||
|
free(file_buff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result == 0)
|
||||||
|
{
|
||||||
|
printf("download file success, " \
|
||||||
|
"file size="INT64_PRINTF_FORMAT", file save to %s\n", \
|
||||||
|
file_size, local_filename);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("download file fail, " \
|
||||||
|
"error no: %d, error info: %s\n", \
|
||||||
|
result, STRERROR(result));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (strcmp(operation, "getmeta") == 0)
|
||||||
|
{
|
||||||
|
if ((result=storage_get_metadata1(pTrackerServer, \
|
||||||
|
NULL, file_id, \
|
||||||
|
&pMetaList, &meta_count)) == 0)
|
||||||
|
{
|
||||||
|
printf("get meta data success, " \
|
||||||
|
"meta count=%d\n", meta_count);
|
||||||
|
for (i=0; i<meta_count; i++)
|
||||||
|
{
|
||||||
|
printf("%s=%s\n", \
|
||||||
|
pMetaList[i].name, \
|
||||||
|
pMetaList[i].value);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(pMetaList);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("getmeta fail, " \
|
||||||
|
"error no: %d, error info: %s\n", \
|
||||||
|
result, STRERROR(result));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (strcmp(operation, "setmeta") == 0)
|
||||||
|
{
|
||||||
|
if (argc < 6)
|
||||||
|
{
|
||||||
|
printf("Usage: %s <config_file> %s " \
|
||||||
|
"<file_id> " \
|
||||||
|
"<op_flag> <metadata_list>\n" \
|
||||||
|
"\top_flag: %c for overwrite, " \
|
||||||
|
"%c for merge\n" \
|
||||||
|
"\tmetadata_list: name1=value1," \
|
||||||
|
"name2=value2,...\n", \
|
||||||
|
argv[0], operation, \
|
||||||
|
STORAGE_SET_METADATA_FLAG_OVERWRITE, \
|
||||||
|
STORAGE_SET_METADATA_FLAG_MERGE);
|
||||||
|
fdfs_client_destroy();
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
meta_buff = strdup(argv[5]);
|
||||||
|
if (meta_buff == NULL)
|
||||||
|
{
|
||||||
|
printf("Out of memory!\n");
|
||||||
|
return ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
pMetaList = fdfs_split_metadata_ex(meta_buff, \
|
||||||
|
',', '=', &meta_count, &result);
|
||||||
|
if (pMetaList == NULL)
|
||||||
|
{
|
||||||
|
printf("Out of memory!\n");
|
||||||
|
free(meta_buff);
|
||||||
|
return ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((result=storage_set_metadata1(pTrackerServer, \
|
||||||
|
NULL, file_id, \
|
||||||
|
pMetaList, meta_count, *argv[4])) == 0)
|
||||||
|
{
|
||||||
|
printf("set meta data success\n");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("setmeta fail, " \
|
||||||
|
"error no: %d, error info: %s\n", \
|
||||||
|
result, STRERROR(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
free(meta_buff);
|
||||||
|
free(pMetaList);
|
||||||
|
}
|
||||||
|
else if(strcmp(operation, "delete") == 0)
|
||||||
|
{
|
||||||
|
if ((result=storage_delete_file1(pTrackerServer, \
|
||||||
|
NULL, file_id)) == 0)
|
||||||
|
{
|
||||||
|
printf("delete file success\n");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("delete file fail, " \
|
||||||
|
"error no: %d, error info: %s\n", \
|
||||||
|
result, STRERROR(result));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fdfs_client_destroy();
|
||||||
|
printf("invalid operation: %s\n", operation);
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* for test only */
|
||||||
|
if ((result=fdfs_active_test(pTrackerServer)) != 0)
|
||||||
|
{
|
||||||
|
printf("active_test to tracker server %s:%d fail, errno: %d\n", \
|
||||||
|
pTrackerServer->ip_addr, pTrackerServer->port, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* for test only */
|
||||||
|
if ((result=fdfs_active_test(pStorageServer)) != 0)
|
||||||
|
{
|
||||||
|
printf("active_test to storage server %s:%d fail, errno: %d\n", \
|
||||||
|
pStorageServer->ip_addr, pStorageServer->port, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
tracker_disconnect_server_ex(pStorageServer, true);
|
||||||
|
tracker_disconnect_server_ex(pTrackerServer, true);
|
||||||
|
|
||||||
|
fdfs_client_destroy();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,88 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2008 Happy Fish / YuQing
|
||||||
|
*
|
||||||
|
* FastDFS may be copied only under the terms of the GNU General
|
||||||
|
* Public License V3, which may be found in the FastDFS source kit.
|
||||||
|
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||||
|
**/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include "fdfs_client.h"
|
||||||
|
#include "logger.h"
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
char *conf_filename;
|
||||||
|
char *local_filename;
|
||||||
|
char group_name[FDFS_GROUP_NAME_MAX_LEN + 1];
|
||||||
|
ConnectionInfo *pTrackerServer;
|
||||||
|
int result;
|
||||||
|
int store_path_index;
|
||||||
|
ConnectionInfo storageServer;
|
||||||
|
char file_id[128];
|
||||||
|
|
||||||
|
if (argc < 3)
|
||||||
|
{
|
||||||
|
printf("Usage: %s <config_file> <local_filename>\n", argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_init();
|
||||||
|
g_log_context.log_level = LOG_ERR;
|
||||||
|
ignore_signal_pipe();
|
||||||
|
|
||||||
|
conf_filename = argv[1];
|
||||||
|
if ((result=fdfs_client_init(conf_filename)) != 0)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
pTrackerServer = tracker_get_connection();
|
||||||
|
if (pTrackerServer == NULL)
|
||||||
|
{
|
||||||
|
fdfs_client_destroy();
|
||||||
|
return errno != 0 ? errno : ECONNREFUSED;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
*group_name = '\0';
|
||||||
|
store_path_index = 0;
|
||||||
|
if ((result=tracker_query_storage_store(pTrackerServer, \
|
||||||
|
&storageServer, group_name, &store_path_index)) != 0)
|
||||||
|
{
|
||||||
|
fdfs_client_destroy();
|
||||||
|
fprintf(stderr, "tracker_query_storage fail, " \
|
||||||
|
"error no: %d, error info: %s\n", \
|
||||||
|
result, STRERROR(result));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
local_filename = argv[2];
|
||||||
|
result = storage_upload_appender_by_filename1(pTrackerServer, \
|
||||||
|
&storageServer, store_path_index, \
|
||||||
|
local_filename, NULL, \
|
||||||
|
NULL, 0, group_name, file_id);
|
||||||
|
if (result != 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "upload file fail, " \
|
||||||
|
"error no: %d, error info: %s\n", \
|
||||||
|
result, STRERROR(result));
|
||||||
|
|
||||||
|
tracker_disconnect_server_ex(pTrackerServer, true);
|
||||||
|
fdfs_client_destroy();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("%s\n", file_id);
|
||||||
|
|
||||||
|
tracker_disconnect_server_ex(pTrackerServer, true);
|
||||||
|
fdfs_client_destroy();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,120 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2008 Happy Fish / YuQing
|
||||||
|
*
|
||||||
|
* FastDFS may be copied only under the terms of the GNU General
|
||||||
|
* Public License V3, which may be found in the FastDFS source kit.
|
||||||
|
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||||
|
**/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include "fdfs_client.h"
|
||||||
|
#include "logger.h"
|
||||||
|
|
||||||
|
static void usage(char *argv[])
|
||||||
|
{
|
||||||
|
printf("Usage: %s <config_file> <local_filename> " \
|
||||||
|
"[storage_ip:port] [store_path_index]\n", argv[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
char *conf_filename;
|
||||||
|
char *local_filename;
|
||||||
|
char group_name[FDFS_GROUP_NAME_MAX_LEN + 1];
|
||||||
|
ConnectionInfo *pTrackerServer;
|
||||||
|
int result;
|
||||||
|
int store_path_index;
|
||||||
|
ConnectionInfo storageServer;
|
||||||
|
char file_id[128];
|
||||||
|
|
||||||
|
if (argc < 3)
|
||||||
|
{
|
||||||
|
usage(argv);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_init();
|
||||||
|
g_log_context.log_level = LOG_ERR;
|
||||||
|
ignore_signal_pipe();
|
||||||
|
|
||||||
|
conf_filename = argv[1];
|
||||||
|
if ((result=fdfs_client_init(conf_filename)) != 0)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
pTrackerServer = tracker_get_connection();
|
||||||
|
if (pTrackerServer == NULL)
|
||||||
|
{
|
||||||
|
fdfs_client_destroy();
|
||||||
|
return errno != 0 ? errno : ECONNREFUSED;
|
||||||
|
}
|
||||||
|
|
||||||
|
local_filename = argv[2];
|
||||||
|
*group_name = '\0';
|
||||||
|
if (argc >= 4)
|
||||||
|
{
|
||||||
|
const char *pPort;
|
||||||
|
const char *pIpAndPort;
|
||||||
|
|
||||||
|
pIpAndPort = argv[3];
|
||||||
|
pPort = strchr(pIpAndPort, ':');
|
||||||
|
if (pPort == NULL)
|
||||||
|
{
|
||||||
|
fdfs_client_destroy();
|
||||||
|
fprintf(stderr, "invalid storage ip address and " \
|
||||||
|
"port: %s\n", pIpAndPort);
|
||||||
|
usage(argv);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
storageServer.sock = -1;
|
||||||
|
snprintf(storageServer.ip_addr, sizeof(storageServer.ip_addr), \
|
||||||
|
"%.*s", (int)(pPort - pIpAndPort), pIpAndPort);
|
||||||
|
storageServer.port = atoi(pPort + 1);
|
||||||
|
if (argc >= 5)
|
||||||
|
{
|
||||||
|
store_path_index = atoi(argv[4]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
store_path_index = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ((result=tracker_query_storage_store(pTrackerServer, \
|
||||||
|
&storageServer, group_name, &store_path_index)) != 0)
|
||||||
|
{
|
||||||
|
fdfs_client_destroy();
|
||||||
|
fprintf(stderr, "tracker_query_storage fail, " \
|
||||||
|
"error no: %d, error info: %s\n", \
|
||||||
|
result, STRERROR(result));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = storage_upload_by_filename1(pTrackerServer, \
|
||||||
|
&storageServer, store_path_index, \
|
||||||
|
local_filename, NULL, \
|
||||||
|
NULL, 0, group_name, file_id);
|
||||||
|
if (result == 0)
|
||||||
|
{
|
||||||
|
printf("%s\n", file_id);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf(stderr, "upload file fail, " \
|
||||||
|
"error no: %d, error info: %s\n", \
|
||||||
|
result, STRERROR(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
tracker_disconnect_server_ex(pTrackerServer, true);
|
||||||
|
fdfs_client_destroy();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,571 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2008 Happy Fish / YuQing
|
||||||
|
*
|
||||||
|
* FastDFS may be copied only under the terms of the GNU General
|
||||||
|
* Public License V3, which may be found in the FastDFS source kit.
|
||||||
|
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||||
|
**/
|
||||||
|
|
||||||
|
#ifndef STORAGE_CLIENT_H
|
||||||
|
#define STORAGE_CLIENT_H
|
||||||
|
|
||||||
|
#include "tracker_types.h"
|
||||||
|
#include "client_func.h"
|
||||||
|
|
||||||
|
#define FDFS_DOWNLOAD_TO_BUFF 1
|
||||||
|
#define FDFS_DOWNLOAD_TO_FILE 2
|
||||||
|
#define FDFS_DOWNLOAD_TO_CALLBACK 3
|
||||||
|
|
||||||
|
#define FDFS_UPLOAD_BY_BUFF 1
|
||||||
|
#define FDFS_UPLOAD_BY_FILE 2
|
||||||
|
#define FDFS_UPLOAD_BY_CALLBACK 3
|
||||||
|
|
||||||
|
#define FDFS_FILE_ID_SEPERATOR '/'
|
||||||
|
#define FDFS_FILE_ID_SEPERATE_STR "/"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define storage_upload_by_filename(pTrackerServer, \
|
||||||
|
pStorageServer, store_path_index, local_filename, \
|
||||||
|
file_ext_name, meta_list, meta_count, group_name, \
|
||||||
|
remote_filename) \
|
||||||
|
storage_upload_by_filename_ex(pTrackerServer, \
|
||||||
|
pStorageServer, store_path_index, \
|
||||||
|
STORAGE_PROTO_CMD_UPLOAD_FILE, local_filename, \
|
||||||
|
file_ext_name, meta_list, meta_count, group_name, \
|
||||||
|
remote_filename)
|
||||||
|
|
||||||
|
#define storage_upload_appender_by_filename(pTrackerServer, \
|
||||||
|
pStorageServer, store_path_index, local_filename, \
|
||||||
|
file_ext_name, meta_list, meta_count, group_name, \
|
||||||
|
remote_filename) \
|
||||||
|
storage_upload_by_filename_ex(pTrackerServer, \
|
||||||
|
pStorageServer, store_path_index, \
|
||||||
|
STORAGE_PROTO_CMD_UPLOAD_APPENDER_FILE, local_filename, \
|
||||||
|
file_ext_name, meta_list, meta_count, group_name, \
|
||||||
|
remote_filename)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* upload file to storage server (by file name)
|
||||||
|
* params:
|
||||||
|
* pTrackerServer: tracker server
|
||||||
|
* pStorageServer: storage server
|
||||||
|
* store_path_index: the index of path on the storage server
|
||||||
|
* local_filename: local filename to upload
|
||||||
|
* cmd: the protocol command
|
||||||
|
* file_ext_name: file ext name, not include dot(.),
|
||||||
|
* if be NULL will abstract ext name from the local filename
|
||||||
|
* meta_list: meta info array
|
||||||
|
* meta_count: meta item count
|
||||||
|
* group_name: if not empty, specify the group name.
|
||||||
|
return the group name to store the file
|
||||||
|
* remote_filename: return the new created filename
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
int storage_upload_by_filename_ex(ConnectionInfo *pTrackerServer, \
|
||||||
|
ConnectionInfo *pStorageServer, const int store_path_index, \
|
||||||
|
const char cmd, const char *local_filename, \
|
||||||
|
const char *file_ext_name, const FDFSMetaData *meta_list, \
|
||||||
|
const int meta_count, char *group_name, char *remote_filename);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* upload file to storage server (by file buff)
|
||||||
|
* params:
|
||||||
|
* pTrackerServer: tracker server
|
||||||
|
* pStorageServer: storage server
|
||||||
|
* store_path_index: the index of path on the storage server
|
||||||
|
* file_buff: file content/buff
|
||||||
|
* file_size: file size (bytes)
|
||||||
|
* file_ext_name: file ext name, not include dot(.), can be NULL
|
||||||
|
* meta_list: meta info array
|
||||||
|
* meta_count: meta item count
|
||||||
|
* group_name: if not empty, specify the group name.
|
||||||
|
return the group name to store the file
|
||||||
|
* remote_filename: return the new created filename
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
#define storage_upload_by_filebuff(pTrackerServer, pStorageServer, \
|
||||||
|
store_path_index, file_buff, \
|
||||||
|
file_size, file_ext_name, meta_list, meta_count, \
|
||||||
|
group_name, remote_filename) \
|
||||||
|
storage_do_upload_file(pTrackerServer, pStorageServer, \
|
||||||
|
store_path_index, STORAGE_PROTO_CMD_UPLOAD_FILE, \
|
||||||
|
FDFS_UPLOAD_BY_BUFF, file_buff, NULL, \
|
||||||
|
file_size, NULL, NULL, file_ext_name, meta_list, meta_count, \
|
||||||
|
group_name, remote_filename)
|
||||||
|
|
||||||
|
#define storage_upload_appender_by_filebuff(pTrackerServer, pStorageServer, \
|
||||||
|
store_path_index, file_buff, \
|
||||||
|
file_size, file_ext_name, meta_list, meta_count, \
|
||||||
|
group_name, remote_filename) \
|
||||||
|
storage_do_upload_file(pTrackerServer, pStorageServer, \
|
||||||
|
store_path_index, STORAGE_PROTO_CMD_UPLOAD_APPENDER_FILE, \
|
||||||
|
FDFS_UPLOAD_BY_BUFF, file_buff, NULL, \
|
||||||
|
file_size, NULL, NULL, file_ext_name, meta_list, meta_count, \
|
||||||
|
group_name, remote_filename)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Upload file callback function prototype
|
||||||
|
* params:
|
||||||
|
* arg: callback extra arguement
|
||||||
|
* sock: connected storage socket for sending file content
|
||||||
|
* return: 0 success, !=0 fail, should return the error code
|
||||||
|
**/
|
||||||
|
typedef int (*UploadCallback) (void *arg, const int64_t file_size, int sock);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* upload file to storage server (by callback)
|
||||||
|
* params:
|
||||||
|
* pTrackerServer: tracker server
|
||||||
|
* pStorageServer: storage server
|
||||||
|
* store_path_index: the index of path on the storage server
|
||||||
|
* callback: callback function to send file content to storage server
|
||||||
|
* arg: callback extra arguement
|
||||||
|
* file_size: the file size
|
||||||
|
* file_ext_name: file ext name, not include dot(.), can be NULL
|
||||||
|
* meta_list: meta info array
|
||||||
|
* meta_count: meta item count
|
||||||
|
* group_name: if not empty, specify the group name.
|
||||||
|
return the group name to store the file
|
||||||
|
* remote_filename: return the new created filename
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
#define storage_upload_by_callback(pTrackerServer, pStorageServer, \
|
||||||
|
store_path_index, callback, arg, file_size, file_ext_name, \
|
||||||
|
meta_list, meta_count, group_name, remote_filename) \
|
||||||
|
storage_upload_by_callback_ex(pTrackerServer, pStorageServer, \
|
||||||
|
store_path_index, STORAGE_PROTO_CMD_UPLOAD_FILE, \
|
||||||
|
callback, arg, file_size, file_ext_name, meta_list, \
|
||||||
|
meta_count, group_name, remote_filename)
|
||||||
|
|
||||||
|
#define storage_upload_appender_by_callback(pTrackerServer, pStorageServer, \
|
||||||
|
store_path_index, callback, arg, file_size, file_ext_name, \
|
||||||
|
meta_list, meta_count, group_name, remote_filename) \
|
||||||
|
storage_upload_by_callback_ex(pTrackerServer, pStorageServer, \
|
||||||
|
store_path_index, STORAGE_PROTO_CMD_UPLOAD_APPENDER_FILE, \
|
||||||
|
callback, arg, file_size, file_ext_name, meta_list, \
|
||||||
|
meta_count, group_name, remote_filename)
|
||||||
|
|
||||||
|
int storage_upload_by_callback_ex(ConnectionInfo *pTrackerServer, \
|
||||||
|
ConnectionInfo *pStorageServer, const int store_path_index, \
|
||||||
|
const char cmd, UploadCallback callback, void *arg, \
|
||||||
|
const int64_t file_size, const char *file_ext_name, \
|
||||||
|
const FDFSMetaData *meta_list, const int meta_count, \
|
||||||
|
char *group_name, char *remote_filename);
|
||||||
|
|
||||||
|
int storage_do_upload_file(ConnectionInfo *pTrackerServer, \
|
||||||
|
ConnectionInfo *pStorageServer, const int store_path_index, \
|
||||||
|
const char cmd, const int upload_type, const char *file_buff, \
|
||||||
|
void *arg, const int64_t file_size, const char *master_filename, \
|
||||||
|
const char *prefix_name, const char *file_ext_name, \
|
||||||
|
const FDFSMetaData *meta_list, const int meta_count, \
|
||||||
|
char *group_name, char *remote_filename);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* delete file from storage server
|
||||||
|
* params:
|
||||||
|
* pTrackerServer: tracker server
|
||||||
|
* pStorageServer: storage server
|
||||||
|
* group_name: the group name of storage server
|
||||||
|
* filename: filename on storage server
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
int storage_delete_file(ConnectionInfo *pTrackerServer, \
|
||||||
|
ConnectionInfo *pStorageServer, \
|
||||||
|
const char *group_name, const char *filename);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* download file from storage server
|
||||||
|
* params:
|
||||||
|
* pTrackerServer: tracker server
|
||||||
|
* pStorageServer: storage server
|
||||||
|
* group_name: the group name of storage server
|
||||||
|
* remote_filename: filename on storage server
|
||||||
|
* file_buff: return file content/buff, must be freed
|
||||||
|
* file_size: return file size (bytes)
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
#define storage_download_file(pTrackerServer, pStorageServer, group_name, \
|
||||||
|
remote_filename, file_buff, file_size) \
|
||||||
|
storage_do_download_file_ex(pTrackerServer, pStorageServer, \
|
||||||
|
FDFS_DOWNLOAD_TO_BUFF, group_name, remote_filename, \
|
||||||
|
0, 0, file_buff, NULL, file_size)
|
||||||
|
|
||||||
|
#define storage_download_file_to_buff(pTrackerServer, pStorageServer, \
|
||||||
|
group_name, remote_filename, file_buff, file_size) \
|
||||||
|
storage_do_download_file_ex(pTrackerServer, pStorageServer, \
|
||||||
|
FDFS_DOWNLOAD_TO_BUFF, group_name, remote_filename, \
|
||||||
|
0, 0, file_buff, NULL, file_size)
|
||||||
|
|
||||||
|
#define storage_do_download_file(pTrackerServer, pStorageServer, \
|
||||||
|
download_type, group_name, remote_filename, \
|
||||||
|
file_buff, arg, file_size) \
|
||||||
|
storage_do_download_file_ex(pTrackerServer, pStorageServer, \
|
||||||
|
download_type, group_name, remote_filename, \
|
||||||
|
0, 0, file_buff, arg, file_size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* download file from storage server
|
||||||
|
* params:
|
||||||
|
* pTrackerServer: tracker server
|
||||||
|
* pStorageServer: storage server
|
||||||
|
* download_type: FDFS_DOWNLOAD_TO_BUFF or FDFS_DOWNLOAD_TO_FILE
|
||||||
|
* or FDFS_DOWNLOAD_TO_CALLBACK
|
||||||
|
* group_name: the group name of storage server
|
||||||
|
* remote_filename: filename on storage server
|
||||||
|
* file_offset: the start offset to download
|
||||||
|
* download_bytes: download bytes, 0 means from start offset to the file end
|
||||||
|
* file_buff: return file content/buff, must be freed
|
||||||
|
* arg: additional argument for callback(valid only when download_tyee
|
||||||
|
* is FDFS_DOWNLOAD_TO_CALLBACK), can be NULL
|
||||||
|
* file_size: return file size (bytes)
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
int storage_do_download_file_ex(ConnectionInfo *pTrackerServer, \
|
||||||
|
ConnectionInfo *pStorageServer, \
|
||||||
|
const int download_type, \
|
||||||
|
const char *group_name, const char *remote_filename, \
|
||||||
|
const int64_t file_offset, const int64_t download_bytes, \
|
||||||
|
char **file_buff, void *arg, int64_t *file_size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* download file from storage server
|
||||||
|
* params:
|
||||||
|
* pTrackerServer: tracker server
|
||||||
|
* pStorageServer: storage server
|
||||||
|
* group_name: the group name of storage server
|
||||||
|
* remote_filename: filename on storage server
|
||||||
|
* local_filename: local filename to write
|
||||||
|
* file_size: return file size (bytes)
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
int storage_download_file_to_file(ConnectionInfo *pTrackerServer, \
|
||||||
|
ConnectionInfo *pStorageServer, \
|
||||||
|
const char *group_name, const char *remote_filename, \
|
||||||
|
const char *local_filename, int64_t *file_size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Download file callback function prototype
|
||||||
|
* params:
|
||||||
|
* arg: callback extra arguement
|
||||||
|
* file_size: file size
|
||||||
|
* data: temp buff, should not keep persistently
|
||||||
|
* current_size: current data size
|
||||||
|
* return: 0 success, !=0 fail, should return the error code
|
||||||
|
**/
|
||||||
|
typedef int (*DownloadCallback) (void *arg, const int64_t file_size, \
|
||||||
|
const char *data, const int current_size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* download file from storage server
|
||||||
|
* params:
|
||||||
|
* pTrackerServer: tracker server
|
||||||
|
* pStorageServer: storage server
|
||||||
|
* group_name: the group name of storage server
|
||||||
|
* remote_filename: filename on storage server
|
||||||
|
* file_offset: the start offset to download
|
||||||
|
* download_bytes: download bytes, 0 means from start offset to the file end
|
||||||
|
* callback: callback function
|
||||||
|
* arg: callback extra arguement
|
||||||
|
* file_size: return file size (bytes)
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
int storage_download_file_ex(ConnectionInfo *pTrackerServer, \
|
||||||
|
ConnectionInfo *pStorageServer, \
|
||||||
|
const char *group_name, const char *remote_filename, \
|
||||||
|
const int64_t file_offset, const int64_t download_bytes, \
|
||||||
|
DownloadCallback callback, void *arg, int64_t *file_size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set metadata items to storage server
|
||||||
|
* params:
|
||||||
|
* pTrackerServer: tracker server
|
||||||
|
* pStorageServer: storage server
|
||||||
|
* group_name: the group name of storage server
|
||||||
|
* filename: filename on storage server
|
||||||
|
* meta_list: meta item array
|
||||||
|
* meta_count: meta item count
|
||||||
|
* op_flag:
|
||||||
|
* # STORAGE_SET_METADATA_FLAG_OVERWRITE('O'): overwrite all old
|
||||||
|
* metadata items
|
||||||
|
* # STORAGE_SET_METADATA_FLAG_MERGE ('M'): merge, insert when
|
||||||
|
* the metadata item not exist, otherwise update it
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
int storage_set_metadata(ConnectionInfo *pTrackerServer, \
|
||||||
|
ConnectionInfo *pStorageServer, \
|
||||||
|
const char *group_name, const char *filename, \
|
||||||
|
const FDFSMetaData *meta_list, const int meta_count, \
|
||||||
|
const char op_flag);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get all metadata items from storage server
|
||||||
|
* params:
|
||||||
|
* pTrackerServer: tracker server
|
||||||
|
* pStorageServer: storage server
|
||||||
|
* group_name: the group name of storage server
|
||||||
|
* filename: filename on storage server
|
||||||
|
* meta_list: return meta info array, must be freed
|
||||||
|
* meta_count: return meta item count
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
int storage_get_metadata(ConnectionInfo *pTrackerServer, \
|
||||||
|
ConnectionInfo *pStorageServer, \
|
||||||
|
const char *group_name, const char *filename, \
|
||||||
|
FDFSMetaData **meta_list, \
|
||||||
|
int *meta_count);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* upload slave file to storage server (by file name)
|
||||||
|
* params:
|
||||||
|
* pTrackerServer: tracker server
|
||||||
|
* pStorageServer: storage server
|
||||||
|
* local_filename: local filename to upload
|
||||||
|
* master_filename: the mater filename to generate the slave file id
|
||||||
|
* prefix_name: the prefix name to generate the slave file id
|
||||||
|
* file_ext_name: file ext name, not include dot(.),
|
||||||
|
* if be NULL will abstract ext name from the local filename
|
||||||
|
* meta_list: meta info array
|
||||||
|
* meta_count: meta item count
|
||||||
|
* group_name: specify the group name.
|
||||||
|
return the group name to store the file
|
||||||
|
* remote_filename: return the new created filename
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
int storage_upload_slave_by_filename(ConnectionInfo *pTrackerServer, \
|
||||||
|
ConnectionInfo *pStorageServer, const char *local_filename,\
|
||||||
|
const char *master_filename, const char *prefix_name, \
|
||||||
|
const char *file_ext_name, \
|
||||||
|
const FDFSMetaData *meta_list, const int meta_count, \
|
||||||
|
char *group_name, char *remote_filename);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* upload slave file to storage server (by file buff)
|
||||||
|
* params:
|
||||||
|
* pTrackerServer: tracker server
|
||||||
|
* pStorageServer: storage server
|
||||||
|
* file_buff: file content/buff
|
||||||
|
* file_size: file size (bytes)
|
||||||
|
* master_filename: the mater filename to generate the slave file id
|
||||||
|
* prefix_name: the prefix name to generate the slave file id
|
||||||
|
* file_ext_name: file ext name, not include dot(.), can be NULL
|
||||||
|
* meta_list: meta info array
|
||||||
|
* meta_count: meta item count
|
||||||
|
* group_name: specify the group name.
|
||||||
|
return the group name to store the file
|
||||||
|
* remote_filename: return the new created filename
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
int storage_upload_slave_by_filebuff(ConnectionInfo *pTrackerServer, \
|
||||||
|
ConnectionInfo *pStorageServer, const char *file_buff, \
|
||||||
|
const int64_t file_size, const char *master_filename, \
|
||||||
|
const char *prefix_name, const char *file_ext_name, \
|
||||||
|
const FDFSMetaData *meta_list, const int meta_count, \
|
||||||
|
char *group_name, char *remote_filename);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* upload slave file to storage server (by callback)
|
||||||
|
* params:
|
||||||
|
* pTrackerServer: tracker server
|
||||||
|
* pStorageServer: storage server
|
||||||
|
* callback: callback function to send file content to storage server
|
||||||
|
* arg: callback extra arguement
|
||||||
|
* file_size: the file size
|
||||||
|
* master_filename: the mater filename to generate the slave file id
|
||||||
|
* prefix_name: the prefix name to generate the slave file id
|
||||||
|
* file_ext_name: file ext name, not include dot(.), can be NULL
|
||||||
|
* meta_list: meta info array
|
||||||
|
* meta_count: meta item count
|
||||||
|
* group_name: specify the group name.
|
||||||
|
return the group name to store the file
|
||||||
|
* remote_filename: return the new created filename
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
int storage_upload_slave_by_callback(ConnectionInfo *pTrackerServer, \
|
||||||
|
ConnectionInfo *pStorageServer, \
|
||||||
|
UploadCallback callback, void *arg, \
|
||||||
|
const int64_t file_size, const char *master_filename, \
|
||||||
|
const char *prefix_name, const char *file_ext_name, \
|
||||||
|
const FDFSMetaData *meta_list, const int meta_count, \
|
||||||
|
char *group_name, char *remote_filename);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* append file to storage server (by local filename)
|
||||||
|
* params:
|
||||||
|
* pTrackerServer: tracker server
|
||||||
|
* pStorageServer: storage server
|
||||||
|
* local_filename: local filename to upload
|
||||||
|
* group_name: the group name
|
||||||
|
* appender_filename: the appender filename
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
int storage_append_by_filename(ConnectionInfo *pTrackerServer, \
|
||||||
|
ConnectionInfo *pStorageServer, const char *local_filename,\
|
||||||
|
const char *group_name, const char *appender_filename);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* append file to storage server (by callback)
|
||||||
|
* params:
|
||||||
|
* pTrackerServer: tracker server
|
||||||
|
* pStorageServer: storage server
|
||||||
|
* callback: callback function to send file content to storage server
|
||||||
|
* arg: callback extra arguement
|
||||||
|
* file_size: the file size
|
||||||
|
* group_name: the group name
|
||||||
|
* appender_filename: the appender filename
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
int storage_append_by_callback(ConnectionInfo *pTrackerServer, \
|
||||||
|
ConnectionInfo *pStorageServer, \
|
||||||
|
UploadCallback callback, void *arg, const int64_t file_size, \
|
||||||
|
const char *group_name, const char *appender_filename);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* append file to storage server (by file buff)
|
||||||
|
* params:
|
||||||
|
* pTrackerServer: tracker server
|
||||||
|
* pStorageServer: storage server
|
||||||
|
* file_buff: file content/buff
|
||||||
|
* file_size: file size (bytes)
|
||||||
|
* group_name: the group name
|
||||||
|
* appender_filename: the appender filename
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
int storage_append_by_filebuff(ConnectionInfo *pTrackerServer, \
|
||||||
|
ConnectionInfo *pStorageServer, const char *file_buff, \
|
||||||
|
const int64_t file_size, const char *group_name, \
|
||||||
|
const char *appender_filename);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* modify file to storage server (by local filename)
|
||||||
|
* params:
|
||||||
|
* pTrackerServer: tracker server
|
||||||
|
* pStorageServer: storage server
|
||||||
|
* local_filename: local filename to upload
|
||||||
|
* file_offset: the start offset to modify appender file
|
||||||
|
* group_name: the group name
|
||||||
|
* appender_filename: the appender filename
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
int storage_modify_by_filename(ConnectionInfo *pTrackerServer, \
|
||||||
|
ConnectionInfo *pStorageServer, const char *local_filename,\
|
||||||
|
const int64_t file_offset, const char *group_name, \
|
||||||
|
const char *appender_filename);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* modify file to storage server (by callback)
|
||||||
|
* params:
|
||||||
|
* pTrackerServer: tracker server
|
||||||
|
* pStorageServer: storage server
|
||||||
|
* callback: callback function to send file content to storage server
|
||||||
|
* arg: callback extra arguement
|
||||||
|
* file_offset: the start offset to modify appender file
|
||||||
|
* file_size: the file size
|
||||||
|
* group_name: the group name
|
||||||
|
* appender_filename: the appender filename
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
int storage_modify_by_callback(ConnectionInfo *pTrackerServer, \
|
||||||
|
ConnectionInfo *pStorageServer, \
|
||||||
|
UploadCallback callback, void *arg, \
|
||||||
|
const int64_t file_offset, const int64_t file_size, \
|
||||||
|
const char *group_name, const char *appender_filename);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* modify file to storage server (by file buff)
|
||||||
|
* params:
|
||||||
|
* pTrackerServer: tracker server
|
||||||
|
* pStorageServer: storage server
|
||||||
|
* file_buff: file content/buff
|
||||||
|
* file_offset: the start offset to modify appender file
|
||||||
|
* file_size: file size (bytes)
|
||||||
|
* group_name: the group name
|
||||||
|
* appender_filename: the appender filename
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
int storage_modify_by_filebuff(ConnectionInfo *pTrackerServer, \
|
||||||
|
ConnectionInfo *pStorageServer, const char *file_buff, \
|
||||||
|
const int64_t file_offset, const int64_t file_size, \
|
||||||
|
const char *group_name, const char *appender_filename);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* truncate file to sepecify size
|
||||||
|
* params:
|
||||||
|
* pTrackerServer: tracker server
|
||||||
|
* pStorageServer: storage server
|
||||||
|
* group_name: the group name
|
||||||
|
* appender_filename: the appender filename
|
||||||
|
* truncated_file_size: truncated file size
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
int storage_truncate_file(ConnectionInfo *pTrackerServer, \
|
||||||
|
ConnectionInfo *pStorageServer,
|
||||||
|
const char *group_name, const char *appender_filename, \
|
||||||
|
const int64_t truncated_file_size);
|
||||||
|
|
||||||
|
|
||||||
|
#define storage_query_file_info(pTrackerServer, pStorageServer, \
|
||||||
|
group_name, filename, pFileInfo) \
|
||||||
|
storage_query_file_info_ex(pTrackerServer, pStorageServer, \
|
||||||
|
group_name, filename, pFileInfo, false)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* query file info
|
||||||
|
* params:
|
||||||
|
* pTrackerServer: tracker server
|
||||||
|
* pStorageServer: storage server
|
||||||
|
* group_name: the group name of storage server
|
||||||
|
* filename: filename on storage server
|
||||||
|
* pFileInfo: return the file info (file size and create timestamp)
|
||||||
|
* bSilence: when this file not exist, do not log error on storage server
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
int storage_query_file_info_ex(ConnectionInfo *pTrackerServer, \
|
||||||
|
ConnectionInfo *pStorageServer, \
|
||||||
|
const char *group_name, const char *filename, \
|
||||||
|
FDFSFileInfo *pFileInfo, const bool bSilence);
|
||||||
|
|
||||||
|
|
||||||
|
#define fdfs_get_file_info(group_name, remote_filename, pFileInfo) \
|
||||||
|
fdfs_get_file_info_ex(group_name, remote_filename, true, pFileInfo)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* check if file exist
|
||||||
|
* params:
|
||||||
|
* pTrackerServer: tracker server
|
||||||
|
* pStorageServer: storage server
|
||||||
|
* group_name: the group name of storage server
|
||||||
|
* remote_filename: filename on storage server
|
||||||
|
* return: 0 file exist, !=0 not exist, return the error code
|
||||||
|
**/
|
||||||
|
int storage_file_exist(ConnectionInfo *pTrackerServer, \
|
||||||
|
ConnectionInfo *pStorageServer, \
|
||||||
|
const char *group_name, const char *remote_filename);
|
||||||
|
/**
|
||||||
|
* get file info from the filename return by storage server
|
||||||
|
* params:
|
||||||
|
* group_name: the group name of storage server
|
||||||
|
* remote_filename: filename on storage server
|
||||||
|
* get_from_server: if get slave file info from storage server
|
||||||
|
* pFileInfo: return the file info
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
int fdfs_get_file_info_ex(const char *group_name, const char *remote_filename, \
|
||||||
|
const bool get_from_server, FDFSFileInfo *pFileInfo);
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
@ -0,0 +1,532 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2008 Happy Fish / YuQing
|
||||||
|
*
|
||||||
|
* FastDFS may be copied only under the terms of the GNU General
|
||||||
|
* Public License V3, which may be found in the FastDFS source kit.
|
||||||
|
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||||
|
**/
|
||||||
|
|
||||||
|
#ifndef STORAGE_CLIENT1_H
|
||||||
|
#define STORAGE_CLIENT1_H
|
||||||
|
|
||||||
|
#include "tracker_types.h"
|
||||||
|
#include "storage_client.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* upload file to storage server (by file name)
|
||||||
|
* params:
|
||||||
|
* pTrackerServer: tracker server
|
||||||
|
* pStorageServer: storage server
|
||||||
|
* store_path_index: the index of path on the storage server
|
||||||
|
* local_filename: local filename to upload
|
||||||
|
* file_ext_name: file ext name, not include dot(.),
|
||||||
|
* if be NULL will abstract ext name from the local filename
|
||||||
|
* meta_list: meta info array
|
||||||
|
* meta_count: meta item count
|
||||||
|
* group_name: specify the group name to upload file to, can be NULL or emtpy
|
||||||
|
* file_id: return the new created file id (including group name and filename)
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
#define storage_upload_by_filename1(pTrackerServer, pStorageServer, \
|
||||||
|
store_path_index, local_filename, file_ext_name, \
|
||||||
|
meta_list, meta_count, group_name, file_id) \
|
||||||
|
storage_upload_by_filename1_ex(pTrackerServer, pStorageServer, \
|
||||||
|
store_path_index, STORAGE_PROTO_CMD_UPLOAD_FILE, \
|
||||||
|
local_filename, file_ext_name, meta_list, meta_count, \
|
||||||
|
group_name, file_id)
|
||||||
|
|
||||||
|
#define storage_upload_appender_by_filename1(pTrackerServer, pStorageServer, \
|
||||||
|
store_path_index, local_filename, file_ext_name, \
|
||||||
|
meta_list, meta_count, group_name, file_id) \
|
||||||
|
storage_upload_by_filename1_ex(pTrackerServer, pStorageServer, \
|
||||||
|
store_path_index, STORAGE_PROTO_CMD_UPLOAD_APPENDER_FILE, \
|
||||||
|
local_filename, file_ext_name, meta_list, meta_count, \
|
||||||
|
group_name, file_id)
|
||||||
|
|
||||||
|
int storage_upload_by_filename1_ex(ConnectionInfo *pTrackerServer, \
|
||||||
|
ConnectionInfo *pStorageServer, const int store_path_index, \
|
||||||
|
const char cmd, const char *local_filename, \
|
||||||
|
const char *file_ext_name, const FDFSMetaData *meta_list, \
|
||||||
|
const int meta_count, const char *group_name, char *file_id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* upload file to storage server (by file buff)
|
||||||
|
* params:
|
||||||
|
* pTrackerServer: tracker server
|
||||||
|
* pStorageServer: storage server
|
||||||
|
* store_path_index: the index of path on the storage server
|
||||||
|
* file_buff: file content/buff
|
||||||
|
* file_size: file size (bytes)
|
||||||
|
* file_ext_name: file ext name, not include dot(.), can be NULL
|
||||||
|
* meta_list: meta info array
|
||||||
|
* meta_count: meta item count
|
||||||
|
* group_name: specify the group name to upload file to, can be NULL or emtpy
|
||||||
|
* file_id: return the new created file id (including group name and filename)
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
#define storage_upload_by_filebuff1(pTrackerServer, pStorageServer, \
|
||||||
|
store_path_index, file_buff, file_size, file_ext_name, \
|
||||||
|
meta_list, meta_count, group_name, file_id) \
|
||||||
|
storage_do_upload_file1(pTrackerServer, pStorageServer, \
|
||||||
|
store_path_index, STORAGE_PROTO_CMD_UPLOAD_FILE, \
|
||||||
|
FDFS_UPLOAD_BY_BUFF, file_buff, NULL, \
|
||||||
|
file_size, file_ext_name, meta_list, meta_count, \
|
||||||
|
group_name, file_id)
|
||||||
|
|
||||||
|
#define storage_upload_appender_by_filebuff1(pTrackerServer, pStorageServer, \
|
||||||
|
store_path_index, file_buff, file_size, file_ext_name, \
|
||||||
|
meta_list, meta_count, group_name, file_id) \
|
||||||
|
storage_do_upload_file1(pTrackerServer, pStorageServer, \
|
||||||
|
store_path_index, STORAGE_PROTO_CMD_UPLOAD_APPENDER_FILE, \
|
||||||
|
FDFS_UPLOAD_BY_BUFF, file_buff, NULL, \
|
||||||
|
file_size, file_ext_name, meta_list, meta_count, \
|
||||||
|
group_name, file_id)
|
||||||
|
|
||||||
|
int storage_do_upload_file1(ConnectionInfo *pTrackerServer, \
|
||||||
|
ConnectionInfo *pStorageServer, const int store_path_index, \
|
||||||
|
const char cmd, const int upload_type, \
|
||||||
|
const char *file_buff, void *arg, const int64_t file_size, \
|
||||||
|
const char *file_ext_name, const FDFSMetaData *meta_list, \
|
||||||
|
const int meta_count, const char *group_name, char *file_id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* upload file to storage server (by callback)
|
||||||
|
* params:
|
||||||
|
* pTrackerServer: tracker server
|
||||||
|
* pStorageServer: storage server
|
||||||
|
* store_path_index: the index of path on the storage server
|
||||||
|
* file_size: the file size
|
||||||
|
* file_ext_name: file ext name, not include dot(.), can be NULL
|
||||||
|
* callback: callback function to send file content to storage server
|
||||||
|
* arg: callback extra arguement
|
||||||
|
* meta_list: meta info array
|
||||||
|
* meta_count: meta item count
|
||||||
|
* group_name: specify the group name to upload file to, can be NULL or emtpy
|
||||||
|
* file_id: return the new created file id (including group name and filename)
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
#define storage_upload_by_callback1(pTrackerServer, pStorageServer, \
|
||||||
|
store_path_index, callback, arg, \
|
||||||
|
file_size, file_ext_name, meta_list, meta_count, \
|
||||||
|
group_name, file_id) \
|
||||||
|
storage_upload_by_callback1_ex(pTrackerServer, pStorageServer, \
|
||||||
|
store_path_index, STORAGE_PROTO_CMD_UPLOAD_FILE, \
|
||||||
|
callback, arg, file_size, file_ext_name, meta_list, \
|
||||||
|
meta_count, group_name, file_id)
|
||||||
|
|
||||||
|
#define storage_upload_appender_by_callback1(pTrackerServer, pStorageServer, \
|
||||||
|
store_path_index, callback, arg, \
|
||||||
|
file_size, file_ext_name, meta_list, meta_count, \
|
||||||
|
group_name, file_id) \
|
||||||
|
storage_upload_by_callback1_ex(pTrackerServer, pStorageServer, \
|
||||||
|
store_path_index, STORAGE_PROTO_CMD_UPLOAD_APPENDER_FILE, \
|
||||||
|
callback, arg, file_size, file_ext_name, meta_list, \
|
||||||
|
meta_count, group_name, file_id)
|
||||||
|
|
||||||
|
int storage_upload_by_callback1_ex(ConnectionInfo *pTrackerServer, \
|
||||||
|
ConnectionInfo *pStorageServer, const int store_path_index, \
|
||||||
|
const char cmd, UploadCallback callback, void *arg, \
|
||||||
|
const int64_t file_size, const char *file_ext_name, \
|
||||||
|
const FDFSMetaData *meta_list, const int meta_count, \
|
||||||
|
const char *group_name, char *file_id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* delete file from storage server
|
||||||
|
* params:
|
||||||
|
* pTrackerServer: tracker server
|
||||||
|
* pStorageServer: storage server
|
||||||
|
* file_id: the file id to deleted (including group name and filename)
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
int storage_delete_file1(ConnectionInfo *pTrackerServer, \
|
||||||
|
ConnectionInfo *pStorageServer, \
|
||||||
|
const char *file_id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* delete file from storage server
|
||||||
|
* params:
|
||||||
|
* pTrackerServer: tracker server
|
||||||
|
* pStorageServer: storage server
|
||||||
|
* appender_file_id: the appender file id
|
||||||
|
* truncated_file_size: the truncated file size
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
int storage_truncate_file1(ConnectionInfo *pTrackerServer, \
|
||||||
|
ConnectionInfo *pStorageServer,
|
||||||
|
const char *appender_file_id, \
|
||||||
|
const int64_t truncated_file_size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set metadata items to storage server
|
||||||
|
* params:
|
||||||
|
* pTrackerServer: tracker server
|
||||||
|
* pStorageServer: storage server
|
||||||
|
* file_id: the file id (including group name and filename)
|
||||||
|
* meta_list: meta item array
|
||||||
|
* meta_count: meta item count
|
||||||
|
* op_flag:
|
||||||
|
* # STORAGE_SET_METADATA_FLAG_OVERWRITE('O'): overwrite all old
|
||||||
|
* metadata items
|
||||||
|
* # STORAGE_SET_METADATA_FLAG_MERGE ('M'): merge, insert when
|
||||||
|
* the metadata item not exist, otherwise update it
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
int storage_set_metadata1(ConnectionInfo *pTrackerServer, \
|
||||||
|
ConnectionInfo *pStorageServer, \
|
||||||
|
const char *file_id, \
|
||||||
|
const FDFSMetaData *meta_list, const int meta_count, \
|
||||||
|
const char op_flag);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* download file from storage server
|
||||||
|
* params:
|
||||||
|
* pTrackerServer: tracker server
|
||||||
|
* pStorageServer: storage server
|
||||||
|
* file_id: the file id (including group name and filename)
|
||||||
|
* file_buff: return file content/buff, must be freed
|
||||||
|
* file_size: return file size (bytes)
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
#define storage_download_file1(pTrackerServer, pStorageServer, file_id, \
|
||||||
|
file_buff, file_size) \
|
||||||
|
storage_do_download_file1_ex(pTrackerServer, pStorageServer, \
|
||||||
|
FDFS_DOWNLOAD_TO_BUFF, file_id, 0, 0, \
|
||||||
|
file_buff, NULL, file_size)
|
||||||
|
|
||||||
|
#define storage_download_file_to_buff1(pTrackerServer, pStorageServer, \
|
||||||
|
file_id, file_buff, file_size) \
|
||||||
|
storage_do_download_file1_ex(pTrackerServer, pStorageServer, \
|
||||||
|
FDFS_DOWNLOAD_TO_BUFF, file_id, 0, 0, \
|
||||||
|
file_buff, NULL, file_size)
|
||||||
|
|
||||||
|
#define storage_do_download_file1(pTrackerServer, pStorageServer, \
|
||||||
|
download_type, file_id, file_buff, file_size) \
|
||||||
|
storage_do_download_file1_ex(pTrackerServer, pStorageServer, \
|
||||||
|
download_type, file_id, \
|
||||||
|
0, 0, file_buff, NULL, file_size)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* download file from storage server
|
||||||
|
* params:
|
||||||
|
* pTrackerServer: tracker server
|
||||||
|
* pStorageServer: storage server
|
||||||
|
* file_id: the file id (including group name and filename)
|
||||||
|
* file_offset: the start offset to download
|
||||||
|
* download_bytes: download bytes, 0 means from start offset to the file end
|
||||||
|
* file_buff: return file content/buff, must be freed
|
||||||
|
* file_size: return file size (bytes)
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
int storage_do_download_file1_ex(ConnectionInfo *pTrackerServer, \
|
||||||
|
ConnectionInfo *pStorageServer, \
|
||||||
|
const int download_type, const char *file_id, \
|
||||||
|
const int64_t file_offset, const int64_t download_bytes, \
|
||||||
|
char **file_buff, void *arg, int64_t *file_size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* download file from storage server
|
||||||
|
* params:
|
||||||
|
* pTrackerServer: tracker server
|
||||||
|
* pStorageServer: storage server
|
||||||
|
* file_id: the file id (including group name and filename)
|
||||||
|
* local_filename: local filename to write
|
||||||
|
* file_size: return file size (bytes)
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
int storage_download_file_to_file1(ConnectionInfo *pTrackerServer, \
|
||||||
|
ConnectionInfo *pStorageServer, \
|
||||||
|
const char *file_id, \
|
||||||
|
const char *local_filename, int64_t *file_size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get all metadata items from storage server
|
||||||
|
* params:
|
||||||
|
* pTrackerServer: tracker server
|
||||||
|
* pStorageServer: storage server
|
||||||
|
* file_id: the file id (including group name and filename)
|
||||||
|
* meta_list: return meta info array, must be freed
|
||||||
|
* meta_count: return meta item count
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
int storage_get_metadata1(ConnectionInfo *pTrackerServer, \
|
||||||
|
ConnectionInfo *pStorageServer, \
|
||||||
|
const char *file_id, \
|
||||||
|
FDFSMetaData **meta_list, int *meta_count);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* download file from storage server
|
||||||
|
* params:
|
||||||
|
* pTrackerServer: tracker server
|
||||||
|
* pStorageServer: storage server
|
||||||
|
* file_id: the file id (including group name and filename)
|
||||||
|
* file_offset: the start offset to download
|
||||||
|
* download_bytes: download bytes, 0 means from start offset to the file end
|
||||||
|
* callback: callback function
|
||||||
|
* arg: callback extra arguement
|
||||||
|
* file_size: return file size (bytes)
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
int storage_download_file_ex1(ConnectionInfo *pTrackerServer, \
|
||||||
|
ConnectionInfo *pStorageServer, \
|
||||||
|
const char *file_id, \
|
||||||
|
const int64_t file_offset, const int64_t download_bytes, \
|
||||||
|
DownloadCallback callback, void *arg, int64_t *file_size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* query storage server to download file
|
||||||
|
* params:
|
||||||
|
* pTrackerServer: tracker server
|
||||||
|
* pStorageServer: return storage server
|
||||||
|
* file_id: the file id (including group name and filename)
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
int tracker_query_storage_fetch1(ConnectionInfo *pTrackerServer, \
|
||||||
|
ConnectionInfo *pStorageServer, \
|
||||||
|
const char *file_id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* query storage server to update (delete file and set metadata)
|
||||||
|
* params:
|
||||||
|
* pTrackerServer: tracker server
|
||||||
|
* pStorageServer: return storage server
|
||||||
|
* file_id: the file id (including group name and filename)
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
int tracker_query_storage_update1(ConnectionInfo *pTrackerServer, \
|
||||||
|
ConnectionInfo *pStorageServer, \
|
||||||
|
const char *file_id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* query storage server list to fetch file
|
||||||
|
* params:
|
||||||
|
* pTrackerServer: tracker server
|
||||||
|
* pStorageServer: return storage server
|
||||||
|
* nMaxServerCount: max storage server count
|
||||||
|
* server_count: return storage server count
|
||||||
|
* group_name: the group name of storage server
|
||||||
|
* filename: filename on storage server
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
int tracker_query_storage_list1(ConnectionInfo *pTrackerServer, \
|
||||||
|
ConnectionInfo *pStorageServer, const int nMaxServerCount, \
|
||||||
|
int *server_count, const char *file_id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* upload slave file to storage server (by file name)
|
||||||
|
* params:
|
||||||
|
* pTrackerServer: tracker server
|
||||||
|
* pStorageServer: storage server
|
||||||
|
* local_filename: local filename to upload
|
||||||
|
* master_file_id: the mater file id to generate the slave file id
|
||||||
|
* prefix_name: the prefix name to generate the file id
|
||||||
|
* file_ext_name: file ext name, not include dot(.),
|
||||||
|
* if be NULL will abstract ext name from the local filename
|
||||||
|
* meta_list: meta info array
|
||||||
|
* meta_count: meta item count
|
||||||
|
* file_id: return the slave file id
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
int storage_upload_slave_by_filename1(ConnectionInfo *pTrackerServer, \
|
||||||
|
ConnectionInfo *pStorageServer, const char *local_filename,\
|
||||||
|
const char *master_file_id, const char *prefix_name, \
|
||||||
|
const char *file_ext_name, \
|
||||||
|
const FDFSMetaData *meta_list, const int meta_count, \
|
||||||
|
char *file_id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* upload slave file to storage server (by file buff)
|
||||||
|
* params:
|
||||||
|
* pTrackerServer: tracker server
|
||||||
|
* pStorageServer: storage server
|
||||||
|
* file_buff: file content/buff
|
||||||
|
* file_size: file size (bytes)
|
||||||
|
* master_file_id: the mater file id to generate the slave file id
|
||||||
|
* prefix_name: the prefix name to generate the file id
|
||||||
|
* file_ext_name: file ext name, not include dot(.), can be NULL
|
||||||
|
* meta_list: meta info array
|
||||||
|
* meta_count: meta item count
|
||||||
|
* file_id: return the slave file id
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
int storage_upload_slave_by_filebuff1(ConnectionInfo *pTrackerServer, \
|
||||||
|
ConnectionInfo *pStorageServer, const char *file_buff, \
|
||||||
|
const int64_t file_size, const char *master_file_id, \
|
||||||
|
const char *prefix_name, const char *file_ext_name, \
|
||||||
|
const FDFSMetaData *meta_list, const int meta_count, \
|
||||||
|
char *file_id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* upload slave file to storage server (by callback)
|
||||||
|
* params:
|
||||||
|
* pTrackerServer: tracker server
|
||||||
|
* pStorageServer: storage server
|
||||||
|
* callback: callback function to send file content to storage server
|
||||||
|
* arg: callback extra arguement
|
||||||
|
* file_size: the file size
|
||||||
|
* master_file_id: the mater file id to generate the slave file id
|
||||||
|
* prefix_name: the prefix name to generate the file id
|
||||||
|
* file_ext_name: file ext name, not include dot(.), can be NULL
|
||||||
|
* meta_list: meta info array
|
||||||
|
* meta_count: meta item count
|
||||||
|
* file_id: return the slave file id
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
int storage_upload_slave_by_callback1(ConnectionInfo *pTrackerServer, \
|
||||||
|
ConnectionInfo *pStorageServer, \
|
||||||
|
UploadCallback callback, void *arg, \
|
||||||
|
const int64_t file_size, const char *master_file_id, \
|
||||||
|
const char *prefix_name, const char *file_ext_name, \
|
||||||
|
const FDFSMetaData *meta_list, const int meta_count, \
|
||||||
|
char *file_id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* append file to storage server (by filename)
|
||||||
|
* params:
|
||||||
|
* pTrackerServer: tracker server
|
||||||
|
* pStorageServer: storage server
|
||||||
|
* local_filename: local filename to upload
|
||||||
|
* appender_file_id: the appender file id
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
int storage_append_by_filename1(ConnectionInfo *pTrackerServer, \
|
||||||
|
ConnectionInfo *pStorageServer, const char *local_filename,\
|
||||||
|
const char *appender_file_id);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* append file to storage server (by file buff)
|
||||||
|
* params:
|
||||||
|
* pTrackerServer: tracker server
|
||||||
|
* pStorageServer: storage server
|
||||||
|
* file_buff: file content/buff
|
||||||
|
* file_size: file size (bytes)
|
||||||
|
* appender_file_id: the appender file id
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
int storage_append_by_filebuff1(ConnectionInfo *pTrackerServer, \
|
||||||
|
ConnectionInfo *pStorageServer, const char *file_buff, \
|
||||||
|
const int64_t file_size, const char *appender_file_id);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* append file to storage server (by callback)
|
||||||
|
* params:
|
||||||
|
* pTrackerServer: tracker server
|
||||||
|
* pStorageServer: storage server
|
||||||
|
* callback: callback function to send file content to storage server
|
||||||
|
* arg: callback extra arguement
|
||||||
|
* file_size: the file size
|
||||||
|
* appender_file_id: the appender file id
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
int storage_append_by_callback1(ConnectionInfo *pTrackerServer, \
|
||||||
|
ConnectionInfo *pStorageServer, \
|
||||||
|
UploadCallback callback, void *arg, \
|
||||||
|
const int64_t file_size, const char *appender_file_id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* modify file to storage server (by local filename)
|
||||||
|
* params:
|
||||||
|
* pTrackerServer: tracker server
|
||||||
|
* pStorageServer: storage server
|
||||||
|
* local_filename: local filename to upload
|
||||||
|
* file_offset: the start offset to modify appender file
|
||||||
|
* appender_file_id: the appender file id
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
int storage_modify_by_filename1(ConnectionInfo *pTrackerServer, \
|
||||||
|
ConnectionInfo *pStorageServer, const char *local_filename,\
|
||||||
|
const int64_t file_offset, const char *appender_file_id);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* modify file to storage server (by callback)
|
||||||
|
* params:
|
||||||
|
* pTrackerServer: tracker server
|
||||||
|
* pStorageServer: storage server
|
||||||
|
* callback: callback function to send file content to storage server
|
||||||
|
* arg: callback extra arguement
|
||||||
|
* file_offset: the start offset to modify appender file
|
||||||
|
* file_size: the file size
|
||||||
|
* appender_file_id: the appender file id
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
int storage_modify_by_callback1(ConnectionInfo *pTrackerServer, \
|
||||||
|
ConnectionInfo *pStorageServer, \
|
||||||
|
UploadCallback callback, void *arg, \
|
||||||
|
const int64_t file_offset, const int64_t file_size, \
|
||||||
|
const char *appender_file_id);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* modify file to storage server (by file buff)
|
||||||
|
* params:
|
||||||
|
* pTrackerServer: tracker server
|
||||||
|
* pStorageServer: storage server
|
||||||
|
* file_buff: file content/buff
|
||||||
|
* file_offset: the start offset to modify appender file
|
||||||
|
* file_size: file size (bytes)
|
||||||
|
* appender_file_id: the appender file id
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
int storage_modify_by_filebuff1(ConnectionInfo *pTrackerServer, \
|
||||||
|
ConnectionInfo *pStorageServer, const char *file_buff, \
|
||||||
|
const int64_t file_offset, const int64_t file_size, \
|
||||||
|
const char *appender_file_id);
|
||||||
|
|
||||||
|
|
||||||
|
#define storage_query_file_info1(pTrackerServer, pStorageServer, file_id, \
|
||||||
|
pFileInfo) \
|
||||||
|
storage_query_file_info_ex1(pTrackerServer, pStorageServer, file_id, \
|
||||||
|
pFileInfo, false)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* query file info
|
||||||
|
* params:
|
||||||
|
* pTrackerServer: tracker server
|
||||||
|
* pStorageServer: storage server
|
||||||
|
* file_id: the file id
|
||||||
|
* pFileInfo: return the file info (file size and create timestamp)
|
||||||
|
* bSilence: when this file not exist, do not log error on storage server
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
int storage_query_file_info_ex1(ConnectionInfo *pTrackerServer, \
|
||||||
|
ConnectionInfo *pStorageServer, const char *file_id, \
|
||||||
|
FDFSFileInfo *pFileInfo, const bool bSilence);
|
||||||
|
|
||||||
|
#define fdfs_get_file_info1(file_id, pFileInfo) \
|
||||||
|
fdfs_get_file_info_ex1(file_id, true, pFileInfo)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get file info from the filename return by storage server
|
||||||
|
* params:
|
||||||
|
* file_id: the file id return by storage server
|
||||||
|
* get_from_server: if get slave file info from storage server
|
||||||
|
* pFileInfo: return the file info
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
int fdfs_get_file_info_ex1(const char *file_id, const bool get_from_server, \
|
||||||
|
FDFSFileInfo *pFileInfo);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* check if file exist
|
||||||
|
* params:
|
||||||
|
* pTrackerServer: tracker server
|
||||||
|
* pStorageServer: storage server
|
||||||
|
* file_id: the file id return by storage server
|
||||||
|
* return: 0 file exist, !=0 not exist, return the error code
|
||||||
|
**/
|
||||||
|
int storage_file_exist1(ConnectionInfo *pTrackerServer, \
|
||||||
|
ConnectionInfo *pStorageServer, \
|
||||||
|
const char *file_id);
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
.SUFFIXES: .c .o
|
||||||
|
|
||||||
|
COMPILE = $(CC) -Wall -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE -g -O -DDEBUG_FLAG -DOS_LINUX -DIOEVENT_USE_EPOLL
|
||||||
|
INC_PATH = -I/usr/local/include/fastcommon -I/usr/local/include/fastdfs
|
||||||
|
LIB_PATH = -L/usr/local/lib -lfastcommon -lfdfsclient -lpthread -ldl -rdynamic
|
||||||
|
TARGET_PATH = $(TARGET_PATH)
|
||||||
|
|
||||||
|
ALL_OBJS =
|
||||||
|
|
||||||
|
ALL_PRGS = fdfs_monitor fdfs_test fdfs_test1
|
||||||
|
|
||||||
|
all: $(ALL_OBJS) $(ALL_PRGS)
|
||||||
|
.o:
|
||||||
|
$(COMPILE) -o $@ $< $(SHARED_OBJS) $(LIB_PATH) $(INC_PATH)
|
||||||
|
.c:
|
||||||
|
$(COMPILE) -o $@ $< $(ALL_OBJS) $(LIB_PATH) $(INC_PATH)
|
||||||
|
.c.o:
|
||||||
|
$(COMPILE) -c -o $@ $< $(INC_PATH)
|
||||||
|
install:
|
||||||
|
cp -f $(ALL_PRGS) $(TARGET_PATH)
|
||||||
|
clean:
|
||||||
|
rm -f $(ALL_OBJS) $(ALL_PRGS)
|
||||||
|
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
.SUFFIXES: .c .o
|
||||||
|
|
||||||
|
COMPILE = $(CC) $(CFLAGS)
|
||||||
|
INC_PATH = -I/usr/local/include/fastcommon -I/usr/local/include/fastdfs
|
||||||
|
LIB_PATH = -L/usr/local/lib -lfastcommon -lfdfsclient $(LIBS)
|
||||||
|
TARGET_PATH = $(TARGET_PATH)
|
||||||
|
|
||||||
|
ALL_OBJS =
|
||||||
|
|
||||||
|
ALL_PRGS = fdfs_monitor fdfs_test fdfs_test1
|
||||||
|
|
||||||
|
all: $(ALL_OBJS) $(ALL_PRGS)
|
||||||
|
.o:
|
||||||
|
$(COMPILE) -o $@ $< $(SHARED_OBJS) $(LIB_PATH) $(INC_PATH)
|
||||||
|
.c:
|
||||||
|
$(COMPILE) -o $@ $< $(ALL_OBJS) $(LIB_PATH) $(INC_PATH)
|
||||||
|
.c.o:
|
||||||
|
$(COMPILE) -c -o $@ $< $(INC_PATH)
|
||||||
|
install:
|
||||||
|
cp -f $(ALL_PRGS) $(TARGET_PATH)
|
||||||
|
clean:
|
||||||
|
rm -f $(ALL_OBJS) $(ALL_PRGS)
|
||||||
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
../fdfs_monitor.c
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
../fdfs_test.c
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
../fdfs_test1.c
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,353 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2008 Happy Fish / YuQing
|
||||||
|
*
|
||||||
|
* FastDFS may be copied only under the terms of the GNU General
|
||||||
|
* Public License V3, which may be found in the FastDFS source kit.
|
||||||
|
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||||
|
**/
|
||||||
|
|
||||||
|
#ifndef TRACKER_CLIENT_H
|
||||||
|
#define TRACKER_CLIENT_H
|
||||||
|
|
||||||
|
#include "tracker_types.h"
|
||||||
|
#include "tracker_proto.h"
|
||||||
|
#include "client_global.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
bool if_trunk_server;
|
||||||
|
char status;
|
||||||
|
char id[FDFS_STORAGE_ID_MAX_SIZE];
|
||||||
|
char ip_addr[IP_ADDRESS_SIZE];
|
||||||
|
char src_id[FDFS_STORAGE_ID_MAX_SIZE]; //src storage id
|
||||||
|
char domain_name[FDFS_DOMAIN_NAME_MAX_SIZE]; //http domain name
|
||||||
|
char version[FDFS_VERSION_SIZE];
|
||||||
|
int total_mb; //total disk storage in MB
|
||||||
|
int free_mb; //free disk storage in MB
|
||||||
|
int upload_priority; //upload priority
|
||||||
|
time_t join_time; //storage join timestamp (create timestamp)
|
||||||
|
time_t up_time; //storage service started timestamp
|
||||||
|
int store_path_count; //store base path count of each storage server
|
||||||
|
int subdir_count_per_path;
|
||||||
|
int storage_port;
|
||||||
|
int storage_http_port; //storage http server port
|
||||||
|
int current_write_path; //current write path index
|
||||||
|
FDFSStorageStat stat;
|
||||||
|
} FDFSStorageInfo;
|
||||||
|
|
||||||
|
|
||||||
|
#define CHECK_CONNECTION(pTrackerServer, conn, result, new_connection) \
|
||||||
|
do { \
|
||||||
|
if (pTrackerServer->sock < 0) \
|
||||||
|
{ \
|
||||||
|
if ((conn=tracker_connect_server( \
|
||||||
|
pTrackerServer, &result)) != NULL) \
|
||||||
|
{ \
|
||||||
|
return result; \
|
||||||
|
} \
|
||||||
|
new_connection = true; \
|
||||||
|
} \
|
||||||
|
else \
|
||||||
|
{ \
|
||||||
|
conn = pTrackerServer; \
|
||||||
|
new_connection = false; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
|
||||||
|
#define tracker_get_connection() \
|
||||||
|
tracker_get_connection_ex((&g_tracker_group))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get a connection to tracker server
|
||||||
|
* params:
|
||||||
|
* pTrackerGroup: the tracker group
|
||||||
|
* return: != NULL for success, NULL for fail
|
||||||
|
**/
|
||||||
|
ConnectionInfo *tracker_get_connection_ex(TrackerServerGroup *pTrackerGroup);
|
||||||
|
|
||||||
|
|
||||||
|
#define tracker_get_connection_r(pTrackerServer, err_no) \
|
||||||
|
tracker_get_connection_r_ex((&g_tracker_group), pTrackerServer, err_no)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get a connection to tracker server
|
||||||
|
* params:
|
||||||
|
* pTrackerGroup: the tracker group
|
||||||
|
* pTrackerServer: tracker server
|
||||||
|
* return: 0 success, !=0 fail
|
||||||
|
**/
|
||||||
|
ConnectionInfo *tracker_get_connection_r_ex(TrackerServerGroup *pTrackerGroup, \
|
||||||
|
ConnectionInfo *pTrackerServer, int *err_no);
|
||||||
|
|
||||||
|
#define tracker_get_all_connections() \
|
||||||
|
tracker_get_all_connections_ex((&g_tracker_group))
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get a connection to tracker server without connection pool
|
||||||
|
* params:
|
||||||
|
* pTrackerGroup: the tracker group
|
||||||
|
* return: != NULL for success, NULL for fail
|
||||||
|
**/
|
||||||
|
ConnectionInfo *tracker_get_connection_no_pool( \
|
||||||
|
TrackerServerGroup *pTrackerGroup);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* connect to all tracker servers
|
||||||
|
* params:
|
||||||
|
* pTrackerGroup: the tracker group
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
int tracker_get_all_connections_ex(TrackerServerGroup *pTrackerGroup);
|
||||||
|
|
||||||
|
#define tracker_close_all_connections() \
|
||||||
|
tracker_close_all_connections_ex((&g_tracker_group))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* close all connections to tracker servers
|
||||||
|
* params:
|
||||||
|
* pTrackerGroup: the tracker group
|
||||||
|
* return:
|
||||||
|
**/
|
||||||
|
void tracker_close_all_connections_ex(TrackerServerGroup *pTrackerGroup);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* list one group
|
||||||
|
* params:
|
||||||
|
* pTrackerServer: tracker server
|
||||||
|
* group_name: the group name
|
||||||
|
* pDest: return the group info
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
int tracker_list_one_group(ConnectionInfo *pTrackerServer, \
|
||||||
|
const char *group_name, FDFSGroupStat *pDest);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* list all groups
|
||||||
|
* params:
|
||||||
|
* pTrackerServer: tracker server
|
||||||
|
* group_stats: return group info array
|
||||||
|
* max_groups: max group count(group array capacity)
|
||||||
|
* group_count: return group count
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
int tracker_list_groups(ConnectionInfo *pTrackerServer, \
|
||||||
|
FDFSGroupStat *group_stats, const int max_groups, \
|
||||||
|
int *group_count);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* list all servers of the specified group
|
||||||
|
* params:
|
||||||
|
* pTrackerServer: tracker server
|
||||||
|
* szGroupName: group name to query
|
||||||
|
* szStorageId: the storage id to query, can be NULL or empty
|
||||||
|
* storage_infos: return storage info array
|
||||||
|
* max_storages: max storage count(storage array capacity)
|
||||||
|
* storage_count: return storage count
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
int tracker_list_servers(ConnectionInfo *pTrackerServer, \
|
||||||
|
const char *szGroupName, const char *szStorageId, \
|
||||||
|
FDFSStorageInfo *storage_infos, const int max_storages, \
|
||||||
|
int *storage_count);
|
||||||
|
|
||||||
|
#define tracker_query_storage_store(pTrackerServer, pStorageServer, \
|
||||||
|
group_name, store_path_index) \
|
||||||
|
tracker_query_storage_store_without_group(pTrackerServer, \
|
||||||
|
pStorageServer, group_name, store_path_index)
|
||||||
|
/**
|
||||||
|
* query storage server to upload file
|
||||||
|
* params:
|
||||||
|
* pTrackerServer: tracker server
|
||||||
|
* pStorageServer: return storage server
|
||||||
|
* store_path_index: return the index of path on the storage server
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
int tracker_query_storage_store_without_group(ConnectionInfo *pTrackerServer,
|
||||||
|
ConnectionInfo *pStorageServer, char *group_name,
|
||||||
|
int *store_path_index);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* query storage servers/list to upload file
|
||||||
|
* params:
|
||||||
|
* pTrackerServer: tracker server
|
||||||
|
* storageServers: store the storage server list
|
||||||
|
* nMaxServerCount: max storage server count
|
||||||
|
* storage_count: return the storage server count
|
||||||
|
* store_path_index: return the index of path on the storage server
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
#define tracker_query_storage_store_list_without_group( \
|
||||||
|
pTrackerServer, storageServers, nMaxServerCount, \
|
||||||
|
storage_count, group_name, store_path_index) \
|
||||||
|
tracker_query_storage_store_list_with_group( \
|
||||||
|
pTrackerServer, NULL, storageServers, nMaxServerCount, \
|
||||||
|
storage_count, store_path_index)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* query storage server to upload file
|
||||||
|
* params:
|
||||||
|
* pTrackerServer: tracker server
|
||||||
|
* group_name: the group name to upload file to
|
||||||
|
* pStorageServer: return storage server
|
||||||
|
* store_path_index: return the index of path on the storage server
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
int tracker_query_storage_store_with_group(ConnectionInfo *pTrackerServer, \
|
||||||
|
const char *group_name, ConnectionInfo *pStorageServer, \
|
||||||
|
int *store_path_index);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* query storage servers/list to upload file
|
||||||
|
* params:
|
||||||
|
* pTrackerServer: tracker server
|
||||||
|
* group_name: the group name to upload file to
|
||||||
|
* storageServers: store the storage server list
|
||||||
|
* nMaxServerCount: max storage server count
|
||||||
|
* storage_count: return the storage server count
|
||||||
|
* store_path_index: return the index of path on the storage server
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
int tracker_query_storage_store_list_with_group( \
|
||||||
|
ConnectionInfo *pTrackerServer, const char *group_name, \
|
||||||
|
ConnectionInfo *storageServers, const int nMaxServerCount, \
|
||||||
|
int *storage_count, int *store_path_index);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* query storage server to update (delete file or set meta data)
|
||||||
|
* params:
|
||||||
|
* pTrackerServer: tracker server
|
||||||
|
* pStorageServer: return storage server
|
||||||
|
* group_name: the group name of storage server
|
||||||
|
* filename: filename on storage server
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
#define tracker_query_storage_update(pTrackerServer, \
|
||||||
|
pStorageServer, group_name, filename) \
|
||||||
|
tracker_do_query_storage(pTrackerServer, \
|
||||||
|
pStorageServer, TRACKER_PROTO_CMD_SERVICE_QUERY_UPDATE,\
|
||||||
|
group_name, filename)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* query storage server to download file
|
||||||
|
* params:
|
||||||
|
* pTrackerServer: tracker server
|
||||||
|
* pStorageServer: return storage server
|
||||||
|
* group_name: the group name of storage server
|
||||||
|
* filename: filename on storage server
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
#define tracker_query_storage_fetch(pTrackerServer, \
|
||||||
|
pStorageServer, group_name, filename) \
|
||||||
|
tracker_do_query_storage(pTrackerServer, \
|
||||||
|
pStorageServer, TRACKER_PROTO_CMD_SERVICE_QUERY_FETCH_ONE,\
|
||||||
|
group_name, filename)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* query storage server to fetch or update
|
||||||
|
* params:
|
||||||
|
* pTrackerServer: tracker server
|
||||||
|
* pStorageServer: return storage server
|
||||||
|
* cmd : command, TRACKER_PROTO_CMD_SERVICE_QUERY_FETCH_ONE or
|
||||||
|
* TRACKER_PROTO_CMD_SERVICE_QUERY_UPDATE
|
||||||
|
* group_name: the group name of storage server
|
||||||
|
* filename: filename on storage server
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
int tracker_do_query_storage(ConnectionInfo *pTrackerServer, \
|
||||||
|
ConnectionInfo *pStorageServer, const byte cmd, \
|
||||||
|
const char *group_name, const char *filename);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* query storage server list to fetch file
|
||||||
|
* params:
|
||||||
|
* pTrackerServer: tracker server
|
||||||
|
* pStorageServer: return storage server
|
||||||
|
* nMaxServerCount: max storage server count
|
||||||
|
* server_count: return storage server count
|
||||||
|
* group_name: the group name of storage server
|
||||||
|
* filename: filename on storage server
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
int tracker_query_storage_list(ConnectionInfo *pTrackerServer, \
|
||||||
|
ConnectionInfo *pStorageServer, const int nMaxServerCount, \
|
||||||
|
int *server_count, char *group_name, const char *filename);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* delete a storage server from cluster
|
||||||
|
* params:
|
||||||
|
* pTrackerGroup: the tracker group
|
||||||
|
* group_name: the group name which the storage server belongs to
|
||||||
|
* storage_id: the storage server id
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
int tracker_delete_storage(TrackerServerGroup *pTrackerGroup, \
|
||||||
|
const char *group_name, const char *storage_id);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set trunk server of the specified group
|
||||||
|
* params:
|
||||||
|
* pTrackerGroup: the tracker group
|
||||||
|
* group_name: the group name which the storage server belongs to
|
||||||
|
* storage_id: the storage server id, can be NULL or empty
|
||||||
|
* new_trunk_server_id: the new trunk server id
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
int tracker_set_trunk_server(TrackerServerGroup *pTrackerGroup, \
|
||||||
|
const char *group_name, const char *storage_id, \
|
||||||
|
char *new_trunk_server_id);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get storage server status from the tracker server
|
||||||
|
* params:
|
||||||
|
* pTrackerServer: tracker server
|
||||||
|
* group_name: the group name which the storage server belongs to
|
||||||
|
* ip_addr: the ip addr of the storage server
|
||||||
|
* pDestBuff: return the storage server brief info
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
int tracker_get_storage_status(ConnectionInfo *pTrackerServer, \
|
||||||
|
const char *group_name, const char *ip_addr, \
|
||||||
|
FDFSStorageBrief *pDestBuff);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get storage server id from the tracker server
|
||||||
|
* params:
|
||||||
|
* pTrackerServer: tracker server
|
||||||
|
* group_name: the group name which the storage server belongs to
|
||||||
|
* ip_addr: the ip addr of the storage server
|
||||||
|
* storage_id: return the storage server id
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
int tracker_get_storage_id(ConnectionInfo *pTrackerServer, \
|
||||||
|
const char *group_name, const char *ip_addr, \
|
||||||
|
char *storage_id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get storage server highest level status from all tracker servers
|
||||||
|
* params:
|
||||||
|
* pTrackerGroup: the tracker group
|
||||||
|
* group_name: the group name which the storage server belongs to
|
||||||
|
* ip_addr: the ip addr of the storage server
|
||||||
|
* storage_id: return the storage server id
|
||||||
|
* status: return the highest level status
|
||||||
|
* return: 0 success, !=0 fail, return the error code
|
||||||
|
**/
|
||||||
|
int tracker_get_storage_max_status(TrackerServerGroup *pTrackerGroup, \
|
||||||
|
const char *group_name, const char *ip_addr, \
|
||||||
|
char *storage_id, int *status);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
.SUFFIXES: .c .o
|
||||||
|
|
||||||
|
COMPILE = $(CC) -Wall -O2 -D_FILE_OFFSET_BITS=64 -DOS_LINUX
|
||||||
|
#COMPILE = $(CC) -Wall -g -D_FILE_OFFSET_BITS=64 -DOS_LINUX -D__DEBUG__
|
||||||
|
INC_PATH = -I/usr/local/include
|
||||||
|
LIB_PATH = -L/usr/local/lib
|
||||||
|
TARGET_PATH = /usr/local/bin
|
||||||
|
|
||||||
|
COMMON_LIB =
|
||||||
|
SHARED_OBJS = hash.o chain.o shared_func.o ini_file_reader.o \
|
||||||
|
logger.o sockopt.o fdfs_global.o base64.o sched_thread.o \
|
||||||
|
mime_file_parser.o fdfs_http_shared.o
|
||||||
|
|
||||||
|
ALL_OBJS = $(SHARED_OBJS)
|
||||||
|
|
||||||
|
ALL_PRGS =
|
||||||
|
|
||||||
|
all: $(ALL_OBJS) $(ALL_PRGS)
|
||||||
|
.o:
|
||||||
|
$(COMPILE) -o $@ $< $(SHARED_OBJS) $(COMMON_LIB) $(LIB_PATH) $(INC_PATH)
|
||||||
|
.c:
|
||||||
|
$(COMPILE) -o $@ $< $(ALL_OBJS) $(COMMON_LIB) $(LIB_PATH) $(INC_PATH)
|
||||||
|
.c.o:
|
||||||
|
$(COMPILE) -c -o $@ $< $(INC_PATH)
|
||||||
|
install:
|
||||||
|
cp -f $(ALL_PRGS) $(TARGET_PATH)
|
||||||
|
clean:
|
||||||
|
rm -f $(ALL_OBJS) $(ALL_PRGS)
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
#ifndef _OS_BITS_H
|
||||||
|
#define _OS_BITS_H
|
||||||
|
|
||||||
|
#define OS_BITS 64
|
||||||
|
#define OFF_BITS 64
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,789 @@
|
||||||
|
#include "avl_tree.h"
|
||||||
|
|
||||||
|
int avl_tree_init(AVLTreeInfo *tree, FreeDataFunc free_data_func, \
|
||||||
|
CompareFunc compare_func)
|
||||||
|
{
|
||||||
|
tree->root = NULL;
|
||||||
|
tree->free_data_func = free_data_func;
|
||||||
|
tree->compare_func = compare_func;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void avl_tree_destroy_loop(FreeDataFunc free_data_func, \
|
||||||
|
AVLTreeNode *pCurrentNode)
|
||||||
|
{
|
||||||
|
if (pCurrentNode->left != NULL)
|
||||||
|
{
|
||||||
|
avl_tree_destroy_loop(free_data_func, pCurrentNode->left);
|
||||||
|
}
|
||||||
|
if (pCurrentNode->right != NULL)
|
||||||
|
{
|
||||||
|
avl_tree_destroy_loop(free_data_func, pCurrentNode->right);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (free_data_func != NULL)
|
||||||
|
{
|
||||||
|
free_data_func(pCurrentNode->data);
|
||||||
|
}
|
||||||
|
free(pCurrentNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
void avl_tree_destroy(AVLTreeInfo *tree)
|
||||||
|
{
|
||||||
|
if (tree == NULL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tree->root != NULL)
|
||||||
|
{
|
||||||
|
avl_tree_destroy_loop(tree->free_data_func, tree->root);
|
||||||
|
tree->root = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static AVLTreeNode *createTreeNode(AVLTreeNode *pParentNode, void *target_data)
|
||||||
|
{
|
||||||
|
AVLTreeNode *pNewNode;
|
||||||
|
pNewNode = (AVLTreeNode *)malloc(sizeof(AVLTreeNode));
|
||||||
|
if (pNewNode == NULL)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
fprintf(stderr, "file: "__FILE__", line: %d, " \
|
||||||
|
"malloc %d bytes fail!\n", __LINE__, \
|
||||||
|
(int)sizeof(AVLTreeNode));
|
||||||
|
*/
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
pNewNode->left = pNewNode->right = NULL;
|
||||||
|
pNewNode->data = target_data;
|
||||||
|
pNewNode->balance = 0;
|
||||||
|
|
||||||
|
return pNewNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void avlRotateLeft(AVLTreeNode *pRotateNode, AVLTreeNode **ppRaiseNode)
|
||||||
|
{
|
||||||
|
*ppRaiseNode = pRotateNode->right;
|
||||||
|
pRotateNode->right = (*ppRaiseNode)->left;
|
||||||
|
(*ppRaiseNode)->left = pRotateNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void avlRotateRight(AVLTreeNode *pRotateNode, AVLTreeNode **ppRaiseNode)
|
||||||
|
{
|
||||||
|
*ppRaiseNode = pRotateNode->left;
|
||||||
|
pRotateNode->left = (*ppRaiseNode)->right;
|
||||||
|
(*ppRaiseNode)->right = pRotateNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void avlLeftBalanceWhenInsert(AVLTreeNode **pTreeNode, int *taller)
|
||||||
|
{
|
||||||
|
AVLTreeNode *leftsub;
|
||||||
|
AVLTreeNode *rightsub;
|
||||||
|
|
||||||
|
leftsub = (*pTreeNode)->left;
|
||||||
|
switch (leftsub->balance)
|
||||||
|
{
|
||||||
|
case -1 :
|
||||||
|
(*pTreeNode)->balance = leftsub->balance = 0;
|
||||||
|
avlRotateRight (*pTreeNode, pTreeNode);
|
||||||
|
*taller = 0;
|
||||||
|
break;
|
||||||
|
case 0 :
|
||||||
|
break;
|
||||||
|
case 1 :
|
||||||
|
rightsub = leftsub->right;
|
||||||
|
switch ( rightsub->balance )
|
||||||
|
{
|
||||||
|
case -1:
|
||||||
|
(*pTreeNode)->balance = 1;
|
||||||
|
leftsub->balance = 0;
|
||||||
|
break;
|
||||||
|
case 0 :
|
||||||
|
(*pTreeNode)->balance = leftsub->balance = 0;
|
||||||
|
break;
|
||||||
|
case 1 :
|
||||||
|
(*pTreeNode)->balance = 0;
|
||||||
|
leftsub->balance = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
rightsub->balance = 0;
|
||||||
|
avlRotateLeft( leftsub, &((*pTreeNode)->left));
|
||||||
|
avlRotateRight(*pTreeNode, pTreeNode);
|
||||||
|
*taller = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void avlRightBalanceWhenInsert(AVLTreeNode **pTreeNode, int *taller)
|
||||||
|
{
|
||||||
|
AVLTreeNode *rightsub;
|
||||||
|
AVLTreeNode *leftsub;
|
||||||
|
|
||||||
|
rightsub = (*pTreeNode)->right;
|
||||||
|
switch (rightsub->balance)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
(*pTreeNode)->balance = rightsub->balance = 0;
|
||||||
|
avlRotateLeft(*pTreeNode, pTreeNode);
|
||||||
|
*taller = 0;
|
||||||
|
break;
|
||||||
|
case 0:
|
||||||
|
break;
|
||||||
|
case -1:
|
||||||
|
leftsub = rightsub->left;
|
||||||
|
switch (leftsub->balance)
|
||||||
|
{
|
||||||
|
case 1 :
|
||||||
|
(*pTreeNode)->balance = -1;
|
||||||
|
rightsub->balance = 0;
|
||||||
|
break;
|
||||||
|
case 0 :
|
||||||
|
(*pTreeNode)->balance = rightsub->balance = 0;
|
||||||
|
break;
|
||||||
|
case -1 :
|
||||||
|
(*pTreeNode)->balance = 0;
|
||||||
|
rightsub->balance = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
leftsub->balance = 0;
|
||||||
|
avlRotateRight(rightsub, &((*pTreeNode)->right));
|
||||||
|
avlRotateLeft(*pTreeNode, pTreeNode);
|
||||||
|
*taller = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int avl_tree_insert_loop(CompareFunc compare_func, AVLTreeNode **pCurrentNode, \
|
||||||
|
void *target_data, int *taller)
|
||||||
|
{
|
||||||
|
int nCompRes;
|
||||||
|
int success;
|
||||||
|
|
||||||
|
if (*pCurrentNode == NULL)
|
||||||
|
{
|
||||||
|
*pCurrentNode = createTreeNode(*pCurrentNode, target_data);
|
||||||
|
if (*pCurrentNode != NULL)
|
||||||
|
{
|
||||||
|
*taller = 1;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nCompRes = compare_func((*pCurrentNode)->data, target_data);
|
||||||
|
if (nCompRes > 0)
|
||||||
|
{
|
||||||
|
success = avl_tree_insert_loop(compare_func, \
|
||||||
|
&((*pCurrentNode)->left), target_data, taller);
|
||||||
|
if (*taller != 0)
|
||||||
|
{
|
||||||
|
switch ((*pCurrentNode)->balance)
|
||||||
|
{
|
||||||
|
case -1:
|
||||||
|
avlLeftBalanceWhenInsert(pCurrentNode, taller);
|
||||||
|
break;
|
||||||
|
case 0:
|
||||||
|
(*pCurrentNode)->balance = -1;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
(*pCurrentNode)->balance = 0;
|
||||||
|
*taller = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (nCompRes < 0)
|
||||||
|
{
|
||||||
|
success = avl_tree_insert_loop(compare_func, \
|
||||||
|
&((*pCurrentNode)->right), target_data, taller);
|
||||||
|
if (*taller != 0)
|
||||||
|
{
|
||||||
|
switch ((*pCurrentNode)->balance)
|
||||||
|
{
|
||||||
|
case -1:
|
||||||
|
(*pCurrentNode)->balance = 0;
|
||||||
|
*taller = 0;
|
||||||
|
break;
|
||||||
|
case 0:
|
||||||
|
(*pCurrentNode)->balance = 1;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
avlRightBalanceWhenInsert(pCurrentNode, taller);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
int avl_tree_insert(AVLTreeInfo *tree, void *data)
|
||||||
|
{
|
||||||
|
int taller;
|
||||||
|
|
||||||
|
taller = 0;
|
||||||
|
return avl_tree_insert_loop(tree->compare_func, &(tree->root), \
|
||||||
|
data, &taller);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int avl_tree_replace_loop(CompareFunc compare_func, \
|
||||||
|
FreeDataFunc free_data_func, AVLTreeNode **pCurrentNode, \
|
||||||
|
void *target_data, int *taller)
|
||||||
|
{
|
||||||
|
int nCompRes;
|
||||||
|
int success;
|
||||||
|
|
||||||
|
if (*pCurrentNode == NULL )
|
||||||
|
{
|
||||||
|
*pCurrentNode = createTreeNode(*pCurrentNode, target_data);
|
||||||
|
if (*pCurrentNode != NULL)
|
||||||
|
{
|
||||||
|
*taller = 1;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nCompRes = compare_func((*pCurrentNode)->data, target_data);
|
||||||
|
if (nCompRes > 0)
|
||||||
|
{
|
||||||
|
success = avl_tree_replace_loop(compare_func, free_data_func,
|
||||||
|
&((*pCurrentNode)->left), target_data, taller);
|
||||||
|
if (*taller != 0)
|
||||||
|
{
|
||||||
|
switch ((*pCurrentNode)->balance)
|
||||||
|
{
|
||||||
|
case -1:
|
||||||
|
avlLeftBalanceWhenInsert(pCurrentNode, taller);
|
||||||
|
break;
|
||||||
|
case 0:
|
||||||
|
(*pCurrentNode)->balance = -1;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
(*pCurrentNode)->balance = 0;
|
||||||
|
*taller = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (nCompRes < 0)
|
||||||
|
{
|
||||||
|
success = avl_tree_replace_loop(compare_func, free_data_func,
|
||||||
|
&((*pCurrentNode)->right), target_data, taller);
|
||||||
|
if (*taller != 0)
|
||||||
|
{
|
||||||
|
switch ((*pCurrentNode)->balance)
|
||||||
|
{
|
||||||
|
case -1 :
|
||||||
|
(*pCurrentNode)->balance = 0;
|
||||||
|
*taller = 0;
|
||||||
|
break;
|
||||||
|
case 0 :
|
||||||
|
(*pCurrentNode)->balance = 1;
|
||||||
|
break;
|
||||||
|
case 1 :
|
||||||
|
avlRightBalanceWhenInsert(pCurrentNode, taller);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (free_data_func != NULL)
|
||||||
|
{
|
||||||
|
free_data_func((*pCurrentNode)->data);
|
||||||
|
}
|
||||||
|
(*pCurrentNode)->data = target_data;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
int avl_tree_replace(AVLTreeInfo *tree, void *data)
|
||||||
|
{
|
||||||
|
int taller;
|
||||||
|
|
||||||
|
taller = 0;
|
||||||
|
return avl_tree_replace_loop(tree->compare_func, \
|
||||||
|
tree->free_data_func, &(tree->root), data, &taller);
|
||||||
|
}
|
||||||
|
|
||||||
|
static AVLTreeNode *avl_tree_find_loop(CompareFunc compare_func, \
|
||||||
|
AVLTreeNode *pCurrentNode, void *target_data)
|
||||||
|
{
|
||||||
|
int nCompRes;
|
||||||
|
nCompRes = compare_func(pCurrentNode->data, target_data);
|
||||||
|
if (nCompRes > 0)
|
||||||
|
{
|
||||||
|
if (pCurrentNode->left == NULL)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return avl_tree_find_loop(compare_func, \
|
||||||
|
pCurrentNode->left, target_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (nCompRes < 0)
|
||||||
|
{
|
||||||
|
if (pCurrentNode->right == NULL)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return avl_tree_find_loop(compare_func, \
|
||||||
|
pCurrentNode->right, target_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return pCurrentNode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *avl_tree_find_ge_loop(CompareFunc compare_func, \
|
||||||
|
AVLTreeNode *pCurrentNode, void *target_data)
|
||||||
|
{
|
||||||
|
int nCompRes;
|
||||||
|
void *found;
|
||||||
|
|
||||||
|
nCompRes = compare_func(pCurrentNode->data, target_data);
|
||||||
|
if (nCompRes > 0)
|
||||||
|
{
|
||||||
|
if (pCurrentNode->left == NULL)
|
||||||
|
{
|
||||||
|
return pCurrentNode->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
found = avl_tree_find_ge_loop(compare_func, \
|
||||||
|
pCurrentNode->left, target_data);
|
||||||
|
return found != NULL ? found : pCurrentNode->data;
|
||||||
|
}
|
||||||
|
else if (nCompRes < 0)
|
||||||
|
{
|
||||||
|
if (pCurrentNode->right == NULL)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return avl_tree_find_ge_loop(compare_func, \
|
||||||
|
pCurrentNode->right, target_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return pCurrentNode->data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void *avl_tree_find(AVLTreeInfo *tree, void *target_data)
|
||||||
|
{
|
||||||
|
AVLTreeNode *found;
|
||||||
|
|
||||||
|
if (tree->root == NULL)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
found = avl_tree_find_loop(tree->compare_func, \
|
||||||
|
tree->root, target_data);
|
||||||
|
|
||||||
|
return found != NULL ? found->data : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *avl_tree_find_ge(AVLTreeInfo *tree, void *target_data)
|
||||||
|
{
|
||||||
|
void *found;
|
||||||
|
|
||||||
|
if (tree->root == NULL)
|
||||||
|
{
|
||||||
|
found = NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
found = avl_tree_find_ge_loop(tree->compare_func, \
|
||||||
|
tree->root, target_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void avlLeftBalanceWhenDelete(AVLTreeNode **pTreeNode, int *shorter)
|
||||||
|
{
|
||||||
|
AVLTreeNode *leftsub;
|
||||||
|
AVLTreeNode *rightsub;
|
||||||
|
|
||||||
|
leftsub = (*pTreeNode)->left;
|
||||||
|
switch (leftsub->balance)
|
||||||
|
{
|
||||||
|
case -1:
|
||||||
|
(*pTreeNode)->balance = leftsub->balance = 0;
|
||||||
|
avlRotateRight (*pTreeNode, pTreeNode);
|
||||||
|
break;
|
||||||
|
case 0:
|
||||||
|
leftsub->balance = 1;
|
||||||
|
avlRotateRight(*pTreeNode, pTreeNode);
|
||||||
|
*shorter = 0;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
rightsub = leftsub->right;
|
||||||
|
switch ( rightsub->balance )
|
||||||
|
{
|
||||||
|
case -1:
|
||||||
|
(*pTreeNode)->balance = 1;
|
||||||
|
leftsub->balance = 0;
|
||||||
|
break;
|
||||||
|
case 0 :
|
||||||
|
(*pTreeNode)->balance = leftsub->balance = 0;
|
||||||
|
break;
|
||||||
|
case 1 :
|
||||||
|
(*pTreeNode)->balance = 0;
|
||||||
|
leftsub->balance = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
rightsub->balance = 0;
|
||||||
|
avlRotateLeft( leftsub, &((*pTreeNode)->left));
|
||||||
|
avlRotateRight(*pTreeNode, pTreeNode);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void avlRightBalanceWhenDelete(AVLTreeNode **pTreeNode, int *shorter)
|
||||||
|
{
|
||||||
|
AVLTreeNode *rightsub;
|
||||||
|
AVLTreeNode *leftsub;
|
||||||
|
|
||||||
|
rightsub = (*pTreeNode)->right;
|
||||||
|
switch (rightsub->balance)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
(*pTreeNode)->balance = rightsub->balance = 0;
|
||||||
|
avlRotateLeft(*pTreeNode, pTreeNode);
|
||||||
|
break;
|
||||||
|
case 0:
|
||||||
|
rightsub->balance = -1;
|
||||||
|
avlRotateLeft(*pTreeNode, pTreeNode);
|
||||||
|
*shorter = 0;
|
||||||
|
break;
|
||||||
|
case -1:
|
||||||
|
leftsub = rightsub->left;
|
||||||
|
switch (leftsub->balance)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
(*pTreeNode)->balance = -1;
|
||||||
|
rightsub->balance = 0;
|
||||||
|
break;
|
||||||
|
case 0:
|
||||||
|
(*pTreeNode)->balance = rightsub->balance = 0;
|
||||||
|
break;
|
||||||
|
case -1:
|
||||||
|
(*pTreeNode)->balance = 0;
|
||||||
|
rightsub->balance = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
leftsub->balance = 0;
|
||||||
|
avlRotateRight(rightsub, &((*pTreeNode)->right));
|
||||||
|
avlRotateLeft(*pTreeNode, pTreeNode);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int avl_tree_delete_loop(AVLTreeInfo *tree, AVLTreeNode **pCurrentNode,\
|
||||||
|
void *target_data, int *shorter, AVLTreeNode *pDeletedDataNode)
|
||||||
|
{
|
||||||
|
int nCompRes;
|
||||||
|
bool result;
|
||||||
|
AVLTreeNode *leftsub;
|
||||||
|
AVLTreeNode *rightsub;
|
||||||
|
|
||||||
|
if (pDeletedDataNode != NULL)
|
||||||
|
{
|
||||||
|
if ((*pCurrentNode)->right == NULL)
|
||||||
|
{
|
||||||
|
pDeletedDataNode->data = (*pCurrentNode)->data;
|
||||||
|
leftsub = (*pCurrentNode)->left;
|
||||||
|
|
||||||
|
free(*pCurrentNode);
|
||||||
|
*pCurrentNode = leftsub;
|
||||||
|
*shorter = 1;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
nCompRes = -1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nCompRes = tree->compare_func((*pCurrentNode)->data, target_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nCompRes > 0)
|
||||||
|
{
|
||||||
|
if ((*pCurrentNode)->left == NULL)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = avl_tree_delete_loop(tree, &((*pCurrentNode)->left), \
|
||||||
|
target_data, shorter, pDeletedDataNode);
|
||||||
|
if (*shorter != 0)
|
||||||
|
{
|
||||||
|
switch ((*pCurrentNode)->balance)
|
||||||
|
{
|
||||||
|
case -1:
|
||||||
|
(*pCurrentNode)->balance = 0;
|
||||||
|
break;
|
||||||
|
case 0:
|
||||||
|
(*pCurrentNode)->balance = 1;
|
||||||
|
*shorter = 0;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
avlRightBalanceWhenDelete(pCurrentNode, shorter);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
else if (nCompRes < 0)
|
||||||
|
{
|
||||||
|
if ((*pCurrentNode)->right == NULL)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = avl_tree_delete_loop(tree, &((*pCurrentNode)->right), \
|
||||||
|
target_data, shorter, pDeletedDataNode);
|
||||||
|
if (*shorter != 0)
|
||||||
|
{
|
||||||
|
switch ((*pCurrentNode)->balance)
|
||||||
|
{
|
||||||
|
case -1:
|
||||||
|
avlLeftBalanceWhenDelete(pCurrentNode, shorter);
|
||||||
|
break;
|
||||||
|
case 0:
|
||||||
|
(*pCurrentNode)->balance = -1;
|
||||||
|
*shorter = 0;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
(*pCurrentNode)->balance = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (tree->free_data_func != NULL)
|
||||||
|
{
|
||||||
|
tree->free_data_func((*pCurrentNode)->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
leftsub = (*pCurrentNode)->left;
|
||||||
|
rightsub = (*pCurrentNode)->right;
|
||||||
|
if (leftsub == NULL)
|
||||||
|
{
|
||||||
|
free(*pCurrentNode);
|
||||||
|
*pCurrentNode = rightsub;
|
||||||
|
}
|
||||||
|
else if (rightsub == NULL)
|
||||||
|
{
|
||||||
|
free(*pCurrentNode);
|
||||||
|
*pCurrentNode = leftsub;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
avl_tree_delete_loop(tree, &((*pCurrentNode)->left), \
|
||||||
|
target_data, shorter, *pCurrentNode);
|
||||||
|
|
||||||
|
if (*shorter != 0)
|
||||||
|
{
|
||||||
|
switch ((*pCurrentNode)->balance)
|
||||||
|
{
|
||||||
|
case -1:
|
||||||
|
(*pCurrentNode)->balance = 0;
|
||||||
|
break;
|
||||||
|
case 0:
|
||||||
|
(*pCurrentNode)->balance = 1;
|
||||||
|
*shorter = 0;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
avlRightBalanceWhenDelete(pCurrentNode, shorter);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
*shorter = 1;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int avl_tree_delete(AVLTreeInfo *tree, void *data)
|
||||||
|
{
|
||||||
|
int shorter;
|
||||||
|
|
||||||
|
if (tree->root == NULL)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
shorter = 0;
|
||||||
|
return avl_tree_delete_loop(tree, &(tree->root), data, &shorter, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int avl_tree_walk_loop(DataOpFunc data_op_func, \
|
||||||
|
AVLTreeNode *pCurrentNode, void *args)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
|
||||||
|
if (pCurrentNode->left != NULL)
|
||||||
|
{
|
||||||
|
result = avl_tree_walk_loop(data_op_func, \
|
||||||
|
pCurrentNode->left, args);
|
||||||
|
if (result != 0)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((result=data_op_func(pCurrentNode->data, args)) != 0)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
if (pCurrentNode->balance >= -1 && pCurrentNode->balance <= 1)
|
||||||
|
{
|
||||||
|
//printf("==%d\n", pCurrentNode->balance);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("==bad %d!!!!!!!!!!!!\n", pCurrentNode->balance);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (pCurrentNode->right != NULL)
|
||||||
|
{
|
||||||
|
result = avl_tree_walk_loop(data_op_func, \
|
||||||
|
pCurrentNode->right, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int avl_tree_walk(AVLTreeInfo *tree, DataOpFunc data_op_func, void *args)
|
||||||
|
{
|
||||||
|
if (tree->root == NULL)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return avl_tree_walk_loop(data_op_func, tree->root, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void avl_tree_count_loop(AVLTreeNode *pCurrentNode, int *count)
|
||||||
|
{
|
||||||
|
if (pCurrentNode->left != NULL)
|
||||||
|
{
|
||||||
|
avl_tree_count_loop(pCurrentNode->left, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
(*count)++;
|
||||||
|
|
||||||
|
if (pCurrentNode->right != NULL)
|
||||||
|
{
|
||||||
|
avl_tree_count_loop(pCurrentNode->right, count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int avl_tree_count(AVLTreeInfo *tree)
|
||||||
|
{
|
||||||
|
int count;
|
||||||
|
if (tree->root == NULL)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
count = 0;
|
||||||
|
avl_tree_count_loop(tree->root, &count);
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
int avl_tree_depth(AVLTreeInfo *tree)
|
||||||
|
{
|
||||||
|
int depth;
|
||||||
|
AVLTreeNode *pNode;
|
||||||
|
|
||||||
|
if (tree->root == NULL)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
depth = 0;
|
||||||
|
pNode = tree->root;
|
||||||
|
while (pNode != NULL)
|
||||||
|
{
|
||||||
|
if (pNode->balance == -1)
|
||||||
|
{
|
||||||
|
pNode = pNode->left;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pNode = pNode->right;
|
||||||
|
}
|
||||||
|
depth++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return depth;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
static void avl_tree_print_loop(AVLTreeNode *pCurrentNode)
|
||||||
|
{
|
||||||
|
printf("%ld left: %ld, right: %ld, balance: %d\n", pCurrentNode->data,
|
||||||
|
pCurrentNode->left != NULL ? pCurrentNode->left->data : 0,
|
||||||
|
pCurrentNode->right != NULL ? pCurrentNode->right->data : 0,
|
||||||
|
pCurrentNode->balance);
|
||||||
|
|
||||||
|
if (pCurrentNode->left != NULL)
|
||||||
|
{
|
||||||
|
avl_tree_print_loop(pCurrentNode->left);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pCurrentNode->right != NULL)
|
||||||
|
{
|
||||||
|
avl_tree_print_loop(pCurrentNode->right);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void avl_tree_print(AVLTreeInfo *tree)
|
||||||
|
{
|
||||||
|
if (tree->root == NULL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
avl_tree_print_loop(tree->root);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
|
||||||
|
#ifndef AVL_TREE_H
|
||||||
|
#define AVL_TREE_H
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include "common_define.h"
|
||||||
|
|
||||||
|
typedef struct tagAVLTreeNode {
|
||||||
|
void *data;
|
||||||
|
struct tagAVLTreeNode *left;
|
||||||
|
struct tagAVLTreeNode *right;
|
||||||
|
byte balance;
|
||||||
|
} AVLTreeNode;
|
||||||
|
|
||||||
|
typedef int (*DataOpFunc) (void *data, void *args);
|
||||||
|
|
||||||
|
typedef struct tagAVLTreeInfo {
|
||||||
|
AVLTreeNode *root;
|
||||||
|
FreeDataFunc free_data_func;
|
||||||
|
CompareFunc compare_func;
|
||||||
|
} AVLTreeInfo;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int avl_tree_init(AVLTreeInfo *tree, FreeDataFunc free_data_func, \
|
||||||
|
CompareFunc compare_func);
|
||||||
|
void avl_tree_destroy(AVLTreeInfo *tree);
|
||||||
|
|
||||||
|
int avl_tree_insert(AVLTreeInfo *tree, void *data);
|
||||||
|
int avl_tree_replace(AVLTreeInfo *tree, void *data);
|
||||||
|
int avl_tree_delete(AVLTreeInfo *tree, void *data);
|
||||||
|
void *avl_tree_find(AVLTreeInfo *tree, void *target_data);
|
||||||
|
void *avl_tree_find_ge(AVLTreeInfo *tree, void *target_data);
|
||||||
|
int avl_tree_walk(AVLTreeInfo *tree, DataOpFunc data_op_func, void *args);
|
||||||
|
int avl_tree_count(AVLTreeInfo *tree);
|
||||||
|
int avl_tree_depth(AVLTreeInfo *tree);
|
||||||
|
//void avl_tree_print(AVLTreeInfo *tree);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,393 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2008 Happy Fish / YuQing
|
||||||
|
*
|
||||||
|
* FastDFS may be copied only under the terms of the GNU General
|
||||||
|
* Public License V3, which may be found in the FastDFS source kit.
|
||||||
|
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||||
|
**/
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include "base64.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marker value for chars we just ignore, e.g. \n \r high ascii
|
||||||
|
*/
|
||||||
|
#define BASE64_IGNORE -1
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marker for = trailing pad
|
||||||
|
*/
|
||||||
|
#define BASE64_PAD -2
|
||||||
|
|
||||||
|
/**
|
||||||
|
* determines how long the lines are that are generated by encode.
|
||||||
|
* Ignored by decode.
|
||||||
|
* @param length 0 means no newlines inserted. Must be a multiple of 4.
|
||||||
|
*/
|
||||||
|
void base64_set_line_length(struct base64_context *context, const int length)
|
||||||
|
{
|
||||||
|
context->line_length = (length / 4) * 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How lines are separated.
|
||||||
|
* Ignored by decode.
|
||||||
|
* @param context->line_separator may be "" but not null.
|
||||||
|
* Usually contains only a combination of chars \n and \r.
|
||||||
|
* Could be any chars not in set A-Z a-z 0-9 + /.
|
||||||
|
*/
|
||||||
|
void base64_set_line_separator(struct base64_context *context, \
|
||||||
|
const char *pLineSeparator)
|
||||||
|
{
|
||||||
|
context->line_sep_len = snprintf(context->line_separator, \
|
||||||
|
sizeof(context->line_separator), "%s", pLineSeparator);
|
||||||
|
}
|
||||||
|
|
||||||
|
void base64_init_ex(struct base64_context *context, const int nLineLength, \
|
||||||
|
const unsigned char chPlus, const unsigned char chSplash, \
|
||||||
|
const unsigned char chPad)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
memset(context, 0, sizeof(struct base64_context));
|
||||||
|
|
||||||
|
context->line_length = nLineLength;
|
||||||
|
context->line_separator[0] = '\n';
|
||||||
|
context->line_separator[1] = '\0';
|
||||||
|
context->line_sep_len = 1;
|
||||||
|
|
||||||
|
// build translate valueToChar table only once.
|
||||||
|
// 0..25 -> 'A'..'Z'
|
||||||
|
for (i=0; i<=25; i++)
|
||||||
|
{
|
||||||
|
context->valueToChar[i] = (char)('A'+i);
|
||||||
|
}
|
||||||
|
// 26..51 -> 'a'..'z'
|
||||||
|
for (i=0; i<=25; i++ )
|
||||||
|
{
|
||||||
|
context->valueToChar[i+26] = (char)('a'+i);
|
||||||
|
}
|
||||||
|
// 52..61 -> '0'..'9'
|
||||||
|
for (i=0; i<=9; i++ )
|
||||||
|
{
|
||||||
|
context->valueToChar[i+52] = (char)('0'+i);
|
||||||
|
}
|
||||||
|
context->valueToChar[62] = chPlus;
|
||||||
|
context->valueToChar[63] = chSplash;
|
||||||
|
|
||||||
|
memset(context->charToValue, BASE64_IGNORE, sizeof(context->charToValue));
|
||||||
|
for (i=0; i<64; i++ )
|
||||||
|
{
|
||||||
|
context->charToValue[context->valueToChar[i]] = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
context->pad_ch = chPad;
|
||||||
|
context->charToValue[chPad] = BASE64_PAD;
|
||||||
|
}
|
||||||
|
|
||||||
|
int base64_get_encode_length(struct base64_context *context, const int nSrcLen)
|
||||||
|
{
|
||||||
|
// Each group or partial group of 3 bytes becomes four chars
|
||||||
|
// covered quotient
|
||||||
|
int outputLength;
|
||||||
|
|
||||||
|
outputLength = ((nSrcLen + 2) / 3) * 4;
|
||||||
|
|
||||||
|
// account for trailing newlines, on all but the very last line
|
||||||
|
if (context->line_length != 0)
|
||||||
|
{
|
||||||
|
int lines = (outputLength + context->line_length - 1) /
|
||||||
|
context->line_length - 1;
|
||||||
|
if ( lines > 0 )
|
||||||
|
{
|
||||||
|
outputLength += lines * context->line_sep_len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return outputLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encode an arbitrary array of bytes as base64 printable ASCII.
|
||||||
|
* It will be broken into lines of 72 chars each. The last line is not
|
||||||
|
* terminated with a line separator.
|
||||||
|
* The output will always have an even multiple of data characters,
|
||||||
|
* exclusive of \n. It is padded out with =.
|
||||||
|
*/
|
||||||
|
char *base64_encode_ex(struct base64_context *context, const char *src, \
|
||||||
|
const int nSrcLen, char *dest, int *dest_len, const bool bPad)
|
||||||
|
{
|
||||||
|
int linePos;
|
||||||
|
int leftover;
|
||||||
|
int combined;
|
||||||
|
char *pDest;
|
||||||
|
int c0, c1, c2, c3;
|
||||||
|
unsigned char *pRaw;
|
||||||
|
unsigned char *pEnd;
|
||||||
|
const char *ppSrcs[2];
|
||||||
|
int lens[2];
|
||||||
|
char szPad[3];
|
||||||
|
int k;
|
||||||
|
int loop;
|
||||||
|
|
||||||
|
if (nSrcLen <= 0)
|
||||||
|
{
|
||||||
|
*dest = '\0';
|
||||||
|
*dest_len = 0;
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
linePos = 0;
|
||||||
|
lens[0] = (nSrcLen / 3) * 3;
|
||||||
|
lens[1] = 3;
|
||||||
|
leftover = nSrcLen - lens[0];
|
||||||
|
ppSrcs[0] = src;
|
||||||
|
ppSrcs[1] = szPad;
|
||||||
|
|
||||||
|
szPad[0] = szPad[1] = szPad[2] = '\0';
|
||||||
|
switch (leftover)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
default:
|
||||||
|
loop = 1;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
loop = 2;
|
||||||
|
szPad[0] = src[nSrcLen-1];
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
loop = 2;
|
||||||
|
szPad[0] = src[nSrcLen-2];
|
||||||
|
szPad[1] = src[nSrcLen-1];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
pDest = dest;
|
||||||
|
for (k=0; k<loop; k++)
|
||||||
|
{
|
||||||
|
pEnd = (unsigned char *)ppSrcs[k] + lens[k];
|
||||||
|
for (pRaw=(unsigned char *)ppSrcs[k]; pRaw<pEnd; pRaw+=3)
|
||||||
|
{
|
||||||
|
// Start a new line if next 4 chars won't fit on the current line
|
||||||
|
// We can't encapsulete the following code since the variable need to
|
||||||
|
// be local to this incarnation of encode.
|
||||||
|
linePos += 4;
|
||||||
|
if (linePos > context->line_length)
|
||||||
|
{
|
||||||
|
if (context->line_length != 0)
|
||||||
|
{
|
||||||
|
memcpy(pDest, context->line_separator, context->line_sep_len);
|
||||||
|
pDest += context->line_sep_len;
|
||||||
|
}
|
||||||
|
linePos = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get next three bytes in unsigned form lined up,
|
||||||
|
// in big-endian order
|
||||||
|
combined = ((*pRaw) << 16) | ((*(pRaw+1)) << 8) | (*(pRaw+2));
|
||||||
|
|
||||||
|
// break those 24 bits into a 4 groups of 6 bits,
|
||||||
|
// working LSB to MSB.
|
||||||
|
c3 = combined & 0x3f;
|
||||||
|
combined >>= 6;
|
||||||
|
c2 = combined & 0x3f;
|
||||||
|
combined >>= 6;
|
||||||
|
c1 = combined & 0x3f;
|
||||||
|
combined >>= 6;
|
||||||
|
c0 = combined & 0x3f;
|
||||||
|
|
||||||
|
// Translate into the equivalent alpha character
|
||||||
|
// emitting them in big-endian order.
|
||||||
|
*pDest++ = context->valueToChar[c0];
|
||||||
|
*pDest++ = context->valueToChar[c1];
|
||||||
|
*pDest++ = context->valueToChar[c2];
|
||||||
|
*pDest++ = context->valueToChar[c3];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*pDest = '\0';
|
||||||
|
*dest_len = pDest - dest;
|
||||||
|
|
||||||
|
// deal with leftover bytes
|
||||||
|
switch (leftover)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
default:
|
||||||
|
// nothing to do
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
// One leftover byte generates xx==
|
||||||
|
if (bPad)
|
||||||
|
{
|
||||||
|
*(pDest-1) = context->pad_ch;
|
||||||
|
*(pDest-2) = context->pad_ch;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*(pDest-2) = '\0';
|
||||||
|
*dest_len -= 2;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
// Two leftover bytes generates xxx=
|
||||||
|
if (bPad)
|
||||||
|
{
|
||||||
|
*(pDest-1) = context->pad_ch;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*(pDest-1) = '\0';
|
||||||
|
*dest_len -= 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
} // end switch;
|
||||||
|
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *base64_decode_auto(struct base64_context *context, const char *src, \
|
||||||
|
const int nSrcLen, char *dest, int *dest_len)
|
||||||
|
{
|
||||||
|
int nRemain;
|
||||||
|
int nPadLen;
|
||||||
|
int nNewLen;
|
||||||
|
char tmpBuff[256];
|
||||||
|
char *pBuff;
|
||||||
|
|
||||||
|
nRemain = nSrcLen % 4;
|
||||||
|
if (nRemain == 0)
|
||||||
|
{
|
||||||
|
return base64_decode(context, src, nSrcLen, dest, dest_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
nPadLen = 4 - nRemain;
|
||||||
|
nNewLen = nSrcLen + nPadLen;
|
||||||
|
if (nNewLen <= sizeof(tmpBuff))
|
||||||
|
{
|
||||||
|
pBuff = tmpBuff;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pBuff = (char *)malloc(nNewLen);
|
||||||
|
if (pBuff == NULL)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Can't malloc %d bytes\n", \
|
||||||
|
nSrcLen + nPadLen + 1);
|
||||||
|
*dest_len = 0;
|
||||||
|
*dest = '\0';
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(pBuff, src, nSrcLen);
|
||||||
|
memset(pBuff + nSrcLen, context->pad_ch, nPadLen);
|
||||||
|
|
||||||
|
base64_decode(context, pBuff, nNewLen, dest, dest_len);
|
||||||
|
|
||||||
|
if (pBuff != tmpBuff)
|
||||||
|
{
|
||||||
|
free(pBuff);
|
||||||
|
}
|
||||||
|
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* decode a well-formed complete base64 string back into an array of bytes.
|
||||||
|
* It must have an even multiple of 4 data characters (not counting \n),
|
||||||
|
* padded out with = as needed.
|
||||||
|
*/
|
||||||
|
char *base64_decode(struct base64_context *context, const char *src, \
|
||||||
|
const int nSrcLen, char *dest, int *dest_len)
|
||||||
|
{
|
||||||
|
// tracks where we are in a cycle of 4 input chars.
|
||||||
|
int cycle;
|
||||||
|
|
||||||
|
// where we combine 4 groups of 6 bits and take apart as 3 groups of 8.
|
||||||
|
int combined;
|
||||||
|
|
||||||
|
// will be an even multiple of 4 chars, plus some embedded \n
|
||||||
|
int dummies;
|
||||||
|
int value;
|
||||||
|
unsigned char *pSrc;
|
||||||
|
unsigned char *pSrcEnd;
|
||||||
|
char *pDest;
|
||||||
|
|
||||||
|
cycle = 0;
|
||||||
|
combined = 0;
|
||||||
|
dummies = 0;
|
||||||
|
pDest = dest;
|
||||||
|
pSrcEnd = (unsigned char *)src + nSrcLen;
|
||||||
|
for (pSrc=(unsigned char *)src; pSrc<pSrcEnd; pSrc++)
|
||||||
|
{
|
||||||
|
value = context->charToValue[*pSrc];
|
||||||
|
switch (value)
|
||||||
|
{
|
||||||
|
case BASE64_IGNORE:
|
||||||
|
// e.g. \n, just ignore it.
|
||||||
|
break;
|
||||||
|
case BASE64_PAD:
|
||||||
|
value = 0;
|
||||||
|
dummies++;
|
||||||
|
// fallthrough
|
||||||
|
default:
|
||||||
|
/* regular value character */
|
||||||
|
switch (cycle)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
combined = value;
|
||||||
|
cycle = 1;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
combined <<= 6;
|
||||||
|
combined |= value;
|
||||||
|
cycle = 2;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
combined <<= 6;
|
||||||
|
combined |= value;
|
||||||
|
cycle = 3;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
combined <<= 6;
|
||||||
|
combined |= value;
|
||||||
|
// we have just completed a cycle of 4 chars.
|
||||||
|
// the four 6-bit values are in combined in big-endian order
|
||||||
|
// peel them off 8 bits at a time working lsb to msb
|
||||||
|
// to get our original 3 8-bit bytes back
|
||||||
|
|
||||||
|
*pDest++ = (char)(combined >> 16);
|
||||||
|
*pDest++ = (char)((combined & 0x0000FF00) >> 8);
|
||||||
|
*pDest++ = (char)(combined & 0x000000FF);
|
||||||
|
|
||||||
|
cycle = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} // end for
|
||||||
|
|
||||||
|
if (cycle != 0)
|
||||||
|
{
|
||||||
|
*dest = '\0';
|
||||||
|
*dest_len = 0;
|
||||||
|
fprintf(stderr, "Input to decode not an even multiple of " \
|
||||||
|
"4 characters; pad with %c\n", context->pad_ch);
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
*dest_len = (pDest - dest) - dummies;
|
||||||
|
*(dest + (*dest_len)) = '\0';
|
||||||
|
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,134 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2008 Happy Fish / YuQing
|
||||||
|
*
|
||||||
|
* FastDFS may be copied only under the terms of the GNU General
|
||||||
|
* Public License V3, which may be found in the FastDFS source kit.
|
||||||
|
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||||
|
**/
|
||||||
|
|
||||||
|
//base64.h
|
||||||
|
|
||||||
|
#ifndef _BASE64_H
|
||||||
|
#define _BASE64_H
|
||||||
|
|
||||||
|
#include "common_define.h"
|
||||||
|
|
||||||
|
struct base64_context
|
||||||
|
{
|
||||||
|
char line_separator[16];
|
||||||
|
int line_sep_len;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* max chars per line, excluding line_separator. A multiple of 4.
|
||||||
|
*/
|
||||||
|
int line_length;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* letter of the alphabet used to encode binary values 0..63
|
||||||
|
*/
|
||||||
|
unsigned char valueToChar[64];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* binary value encoded by a given letter of the alphabet 0..63
|
||||||
|
*/
|
||||||
|
int charToValue[256];
|
||||||
|
int pad_ch;
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** use stardand base64 charset
|
||||||
|
*/
|
||||||
|
#define base64_init(context, nLineLength) \
|
||||||
|
base64_init_ex(context, nLineLength, '+', '/', '=')
|
||||||
|
|
||||||
|
|
||||||
|
/** stardand base64 encode
|
||||||
|
*/
|
||||||
|
#define base64_encode(context, src, nSrcLen, dest, dest_len) \
|
||||||
|
base64_encode_ex(context, src, nSrcLen, dest, dest_len, true)
|
||||||
|
|
||||||
|
/** base64 init function, before use base64 function, you should call
|
||||||
|
* this function
|
||||||
|
* parameters:
|
||||||
|
* context: the base64 context
|
||||||
|
* nLineLength: length of a line, 0 for never add line seperator
|
||||||
|
* chPlus: specify a char for base64 char plus (+)
|
||||||
|
* chSplash: specify a char for base64 char splash (/)
|
||||||
|
* chPad: specify a char for base64 padding char =
|
||||||
|
* return: none
|
||||||
|
*/
|
||||||
|
void base64_init_ex(struct base64_context *context, const int nLineLength, \
|
||||||
|
const unsigned char chPlus, const unsigned char chSplash, \
|
||||||
|
const unsigned char chPad);
|
||||||
|
|
||||||
|
/** calculate the encoded length of the source buffer
|
||||||
|
* parameters:
|
||||||
|
* context: the base64 context
|
||||||
|
* nSrcLen: the source buffer length
|
||||||
|
* return: the encoded length of the source buffer
|
||||||
|
*/
|
||||||
|
int base64_get_encode_length(struct base64_context *context, const int nSrcLen);
|
||||||
|
|
||||||
|
/** base64 encode buffer
|
||||||
|
* parameters:
|
||||||
|
* context: the base64 context
|
||||||
|
* src: the source buffer
|
||||||
|
* nSrcLen: the source buffer length
|
||||||
|
* dest: the dest buffer
|
||||||
|
* dest_len: return dest buffer length
|
||||||
|
* bPad: if padding
|
||||||
|
* return: the encoded buffer
|
||||||
|
*/
|
||||||
|
char *base64_encode_ex(struct base64_context *context, const char *src, \
|
||||||
|
const int nSrcLen, char *dest, int *dest_len, const bool bPad);
|
||||||
|
|
||||||
|
/** base64 decode buffer, work only with padding source string
|
||||||
|
* parameters:
|
||||||
|
* context: the base64 context
|
||||||
|
* src: the source buffer with padding
|
||||||
|
* nSrcLen: the source buffer length
|
||||||
|
* dest: the dest buffer
|
||||||
|
* dest_len: return dest buffer length
|
||||||
|
* return: the decoded buffer
|
||||||
|
*/
|
||||||
|
char *base64_decode(struct base64_context *context, const char *src, \
|
||||||
|
const int nSrcLen, char *dest, int *dest_len);
|
||||||
|
|
||||||
|
/** base64 decode buffer, can work with no padding source string
|
||||||
|
* parameters:
|
||||||
|
* context: the base64 context
|
||||||
|
* src: the source buffer with padding or no padding
|
||||||
|
* nSrcLen: the source buffer length
|
||||||
|
* dest: the dest buffer
|
||||||
|
* dest_len: return dest buffer length
|
||||||
|
* return: the decoded buffer
|
||||||
|
*/
|
||||||
|
char *base64_decode_auto(struct base64_context *context, const char *src, \
|
||||||
|
const int nSrcLen, char *dest, int *dest_len);
|
||||||
|
|
||||||
|
/** set line separator string, such as \n or \r\n
|
||||||
|
* parameters:
|
||||||
|
* context: the base64 context
|
||||||
|
* pLineSeparator: the line separator string
|
||||||
|
* return: none
|
||||||
|
*/
|
||||||
|
void base64_set_line_separator(struct base64_context *context, \
|
||||||
|
const char *pLineSeparator);
|
||||||
|
|
||||||
|
/** set line length, 0 for never add line separators
|
||||||
|
* parameters:
|
||||||
|
* context: the base64 context
|
||||||
|
* length: the line length
|
||||||
|
* return: none
|
||||||
|
*/
|
||||||
|
void base64_set_line_length(struct base64_context *context, const int length);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
@ -0,0 +1,325 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2008 Happy Fish / YuQing
|
||||||
|
*
|
||||||
|
* FastDFS may be copied only under the terms of the GNU General
|
||||||
|
* Public License V3, which may be found in the FastDFS source kit.
|
||||||
|
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||||
|
**/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include "chain.h"
|
||||||
|
//#include "use_mmalloc.h"
|
||||||
|
|
||||||
|
void chain_init(ChainList *pList, const int type, FreeDataFunc freeDataFunc, \
|
||||||
|
CompareFunc compareFunc)
|
||||||
|
{
|
||||||
|
if (pList == NULL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pList->head = NULL;
|
||||||
|
pList->tail = NULL;
|
||||||
|
pList->type = type;
|
||||||
|
pList->freeDataFunc = freeDataFunc;
|
||||||
|
pList->compareFunc = compareFunc;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void chain_destroy(ChainList *pList)
|
||||||
|
{
|
||||||
|
ChainNode *pNode;
|
||||||
|
ChainNode *pDeleted;
|
||||||
|
if (pList == NULL || pList->head == NULL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pNode = pList->head;
|
||||||
|
while (pNode != NULL)
|
||||||
|
{
|
||||||
|
pDeleted = pNode;
|
||||||
|
pNode = pNode->next;
|
||||||
|
|
||||||
|
freeChainNode(pList, pDeleted);
|
||||||
|
}
|
||||||
|
|
||||||
|
pList->head = pList->tail = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void freeChainNode(ChainList *pList, ChainNode *pChainNode)
|
||||||
|
{
|
||||||
|
if (pList->freeDataFunc != NULL)
|
||||||
|
{
|
||||||
|
pList->freeDataFunc(pChainNode->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(pChainNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
int insertNodePrior(ChainList *pList, void *data)
|
||||||
|
{
|
||||||
|
ChainNode *pNode;
|
||||||
|
if (pList == NULL)
|
||||||
|
{
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
pNode = (ChainNode *)malloc(sizeof(ChainNode));
|
||||||
|
if (pNode == NULL)
|
||||||
|
{
|
||||||
|
return ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
pNode->data = data;
|
||||||
|
pNode->next = pList->head;
|
||||||
|
|
||||||
|
pList->head = pNode;
|
||||||
|
if (pList->tail == NULL)
|
||||||
|
{
|
||||||
|
pList->tail = pNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int appendNode(ChainList *pList, void *data)
|
||||||
|
{
|
||||||
|
ChainNode *pNode;
|
||||||
|
if (pList == NULL)
|
||||||
|
{
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
pNode = (ChainNode *)malloc(sizeof(ChainNode));
|
||||||
|
if (pNode == NULL)
|
||||||
|
{
|
||||||
|
return ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
pNode->data = data;
|
||||||
|
pNode->next = NULL;
|
||||||
|
|
||||||
|
if (pList->tail != NULL)
|
||||||
|
{
|
||||||
|
pList->tail->next = pNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
pList->tail = pNode;
|
||||||
|
if (pList->head == NULL)
|
||||||
|
{
|
||||||
|
pList->head = pNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int insertNodeAsc(ChainList *pList, void *data)
|
||||||
|
{
|
||||||
|
ChainNode *pNew;
|
||||||
|
ChainNode *pNode;
|
||||||
|
ChainNode *pPrevious;
|
||||||
|
if (pList == NULL || pList->compareFunc == NULL)
|
||||||
|
{
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
pNew = (ChainNode *)malloc(sizeof(ChainNode));
|
||||||
|
if (pNew == NULL)
|
||||||
|
{
|
||||||
|
return ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
pNew->data = data;
|
||||||
|
|
||||||
|
pPrevious = NULL;
|
||||||
|
pNode = pList->head;
|
||||||
|
while (pNode != NULL && pList->compareFunc(pNode->data, data) < 0)
|
||||||
|
{
|
||||||
|
pPrevious = pNode;
|
||||||
|
pNode = pNode->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
pNew->next = pNode;
|
||||||
|
if (pPrevious == NULL)
|
||||||
|
{
|
||||||
|
pList->head = pNew;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pPrevious->next = pNew;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pNode == NULL)
|
||||||
|
{
|
||||||
|
pList->tail = pNew;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int addNode(ChainList *pList, void *data)
|
||||||
|
{
|
||||||
|
if (pList->type == CHAIN_TYPE_INSERT)
|
||||||
|
{
|
||||||
|
return insertNodePrior(pList, data);
|
||||||
|
}
|
||||||
|
else if (pList->type == CHAIN_TYPE_APPEND)
|
||||||
|
{
|
||||||
|
return appendNode(pList, data);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return insertNodeAsc(pList, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void deleteNodeEx(ChainList *pList, ChainNode *pPreviousNode, \
|
||||||
|
ChainNode *pDeletedNode)
|
||||||
|
{
|
||||||
|
if (pDeletedNode == pList->head)
|
||||||
|
{
|
||||||
|
pList->head = pDeletedNode->next;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pPreviousNode->next = pDeletedNode->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pDeletedNode == pList->tail)
|
||||||
|
{
|
||||||
|
pList->tail = pPreviousNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
freeChainNode(pList, pDeletedNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
void deleteToNodePrevious(ChainList *pList, ChainNode *pPreviousNode, \
|
||||||
|
ChainNode *pDeletedNext)
|
||||||
|
{
|
||||||
|
ChainNode *pNode;
|
||||||
|
ChainNode *pDeletedNode;
|
||||||
|
|
||||||
|
if (pPreviousNode == NULL)
|
||||||
|
{
|
||||||
|
pNode = pList->head;
|
||||||
|
pList->head = pDeletedNext;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pNode = pPreviousNode->next;
|
||||||
|
pPreviousNode->next = pDeletedNext;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (pNode != NULL && pNode != pDeletedNext)
|
||||||
|
{
|
||||||
|
pDeletedNode = pNode;
|
||||||
|
pNode = pNode->next;
|
||||||
|
freeChainNode(pList, pDeletedNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pDeletedNext == NULL)
|
||||||
|
{
|
||||||
|
pList->tail = pPreviousNode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void *chain_pop_head(ChainList *pList)
|
||||||
|
{
|
||||||
|
ChainNode *pDeletedNode;
|
||||||
|
void *data;
|
||||||
|
|
||||||
|
pDeletedNode = pList->head;
|
||||||
|
if (pDeletedNode == NULL)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
pList->head = pDeletedNode->next;
|
||||||
|
if (pList->head == NULL)
|
||||||
|
{
|
||||||
|
pList->tail = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
data = pDeletedNode->data;
|
||||||
|
free(pDeletedNode);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
int chain_count(ChainList *pList)
|
||||||
|
{
|
||||||
|
ChainNode *pNode;
|
||||||
|
int count;
|
||||||
|
|
||||||
|
count = 0;
|
||||||
|
pNode = pList->head;
|
||||||
|
while (pNode != NULL)
|
||||||
|
{
|
||||||
|
count++;
|
||||||
|
pNode = pNode->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
int deleteNode(ChainList *pList, void *data, bool bDeleteAll)
|
||||||
|
{
|
||||||
|
ChainNode *pNode;
|
||||||
|
ChainNode *pPrevious;
|
||||||
|
ChainNode *pDeleted;
|
||||||
|
int nCount;
|
||||||
|
int nCompareRes;
|
||||||
|
|
||||||
|
if (pList == NULL || pList->compareFunc == NULL)
|
||||||
|
{
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
nCount = 0;
|
||||||
|
pPrevious = NULL;
|
||||||
|
pNode = pList->head;
|
||||||
|
while (pNode != NULL)
|
||||||
|
{
|
||||||
|
nCompareRes = pList->compareFunc(pNode->data, data);
|
||||||
|
if (nCompareRes == 0)
|
||||||
|
{
|
||||||
|
pDeleted = pNode;
|
||||||
|
pNode = pNode->next;
|
||||||
|
|
||||||
|
deleteNodeEx(pList, pPrevious, pDeleted);
|
||||||
|
nCount++;
|
||||||
|
|
||||||
|
if (!bDeleteAll)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if(nCompareRes > 0 && pList->type == CHAIN_TYPE_SORTED)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
pPrevious = pNode;
|
||||||
|
pNode = pNode->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
int deleteOne(ChainList *pList, void *data)
|
||||||
|
{
|
||||||
|
return deleteNode(pList, data, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
int deleteAll(ChainList *pList, void *data)
|
||||||
|
{
|
||||||
|
return deleteNode(pList, data, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,148 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2008 Happy Fish / YuQing
|
||||||
|
*
|
||||||
|
* FastDFS may be copied only under the terms of the GNU General
|
||||||
|
* Public License V3, which may be found in the FastDFS source kit.
|
||||||
|
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||||
|
**/
|
||||||
|
|
||||||
|
#ifndef CHAIN_H
|
||||||
|
#define CHAIN_H
|
||||||
|
|
||||||
|
#include "common_define.h"
|
||||||
|
|
||||||
|
#define CHAIN_TYPE_INSERT 0 //insert new node before head
|
||||||
|
#define CHAIN_TYPE_APPEND 1 //insert new node after tail
|
||||||
|
#define CHAIN_TYPE_SORTED 2 //sorted chain
|
||||||
|
|
||||||
|
typedef struct tagChainNode
|
||||||
|
{
|
||||||
|
void *data;
|
||||||
|
struct tagChainNode *next;
|
||||||
|
} ChainNode;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int type;
|
||||||
|
ChainNode *head;
|
||||||
|
ChainNode *tail;
|
||||||
|
FreeDataFunc freeDataFunc;
|
||||||
|
CompareFunc compareFunc;
|
||||||
|
} ChainList;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** chain init function
|
||||||
|
* parameters:
|
||||||
|
* pList: the chain list
|
||||||
|
* type: chain type, value is one of following:
|
||||||
|
* CHAIN_TYPE_INSERT: insert new node before head
|
||||||
|
* CHAIN_TYPE_APPEND: insert new node after tail
|
||||||
|
* CHAIN_TYPE_SORTED: sorted chain
|
||||||
|
* freeDataFunc: free data function pointer, can be NULL
|
||||||
|
* compareFunc: compare data function pointer, can be NULL,
|
||||||
|
* must set when type is CHAIN_TYPE_SORTED
|
||||||
|
* return: none
|
||||||
|
*/
|
||||||
|
void chain_init(ChainList *pList, const int type, FreeDataFunc freeDataFunc, \
|
||||||
|
CompareFunc compareFunc);
|
||||||
|
|
||||||
|
/** destroy chain
|
||||||
|
* parameters:
|
||||||
|
* pList: the chain list
|
||||||
|
* return: none
|
||||||
|
*/
|
||||||
|
void chain_destroy(ChainList *pList);
|
||||||
|
|
||||||
|
|
||||||
|
/** get the chain node count
|
||||||
|
* parameters:
|
||||||
|
* pList: the chain list
|
||||||
|
* return: chain node count
|
||||||
|
*/
|
||||||
|
int chain_count(ChainList *pList);
|
||||||
|
|
||||||
|
/** add a new node to the chain
|
||||||
|
* parameters:
|
||||||
|
* pList: the chain list
|
||||||
|
* data: the data to add
|
||||||
|
* return: error no, 0 for success, != 0 fail
|
||||||
|
*/
|
||||||
|
int addNode(ChainList *pList, void *data);
|
||||||
|
|
||||||
|
/** free the chain node
|
||||||
|
* parameters:
|
||||||
|
* pList: the chain list
|
||||||
|
* pChainNode: the chain node to free
|
||||||
|
* return: none
|
||||||
|
*/
|
||||||
|
void freeChainNode(ChainList *pList, ChainNode *pChainNode);
|
||||||
|
|
||||||
|
/** delete the chain node
|
||||||
|
* parameters:
|
||||||
|
* pList: the chain list
|
||||||
|
* pPreviousNode: the previous chain node
|
||||||
|
* pDeletedNode: the chain node to delete
|
||||||
|
* return: none
|
||||||
|
*/
|
||||||
|
void deleteNodeEx(ChainList *pList, ChainNode *pPreviousNode, \
|
||||||
|
ChainNode *pDeletedNode);
|
||||||
|
|
||||||
|
/** delete the chain nodes from pPreviousNode->next to pDeletedNext
|
||||||
|
* (not including pDeletedNext)
|
||||||
|
* parameters:
|
||||||
|
* pList: the chain list
|
||||||
|
* pPreviousNode: the previous chain node, delete from pPreviousNode->next
|
||||||
|
* pDeletedNext: the chain node after the deleted node
|
||||||
|
* return: none
|
||||||
|
*/
|
||||||
|
void deleteToNodePrevious(ChainList *pList, ChainNode *pPreviousNode, \
|
||||||
|
ChainNode *pDeletedNext);
|
||||||
|
|
||||||
|
/** delete the chain node using data compare function
|
||||||
|
* parameters:
|
||||||
|
* pList: the chain list
|
||||||
|
* data: the first node whose data equals this will be deleted
|
||||||
|
* return: delete chain node count, < 0 fail
|
||||||
|
*/
|
||||||
|
int deleteOne(ChainList *pList, void *data);
|
||||||
|
|
||||||
|
/** delete all chain nodes using data compare function
|
||||||
|
* parameters:
|
||||||
|
* pList: the chain list
|
||||||
|
* data: the node whose data equals this will be deleted
|
||||||
|
* return: delete chain node count, < 0 fail
|
||||||
|
*/
|
||||||
|
int deleteAll(ChainList *pList, void *data);
|
||||||
|
|
||||||
|
/** pop up a chain nodes from chain head
|
||||||
|
* parameters:
|
||||||
|
* pList: the chain list
|
||||||
|
* return: the head node, return NULL when the chain is empty
|
||||||
|
*/
|
||||||
|
void *chain_pop_head(ChainList *pList);
|
||||||
|
|
||||||
|
/** add a chain nodes before the chain head
|
||||||
|
* parameters:
|
||||||
|
* pList: the chain list
|
||||||
|
* data: the node to insert
|
||||||
|
* return: error no, 0 for success, != 0 fail
|
||||||
|
*/
|
||||||
|
int insertNodePrior(ChainList *pList, void *data);
|
||||||
|
|
||||||
|
/** add a chain nodes after the chain tail
|
||||||
|
* parameters:
|
||||||
|
* pList: the chain list
|
||||||
|
* data: the node to insert
|
||||||
|
* return: error no, 0 for success, != 0 fail
|
||||||
|
*/
|
||||||
|
int appendNode(ChainList *pList, void *data);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
@ -0,0 +1,169 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2008 Happy Fish / YuQing
|
||||||
|
*
|
||||||
|
* FastDFS may be copied only under the terms of the GNU General
|
||||||
|
* Public License V3, which may be found in the FastDFS source kit.
|
||||||
|
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||||
|
**/
|
||||||
|
|
||||||
|
//common_define.h
|
||||||
|
|
||||||
|
#ifndef _COMMON_DEFINE_H_
|
||||||
|
#define _COMMON_DEFINE_H_
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
#include <winsock.h>
|
||||||
|
typedef UINT in_addr_t;
|
||||||
|
#define FILE_SEPERATOR "\\"
|
||||||
|
#define THREAD_ENTRANCE_FUNC_DECLARE DWORD WINAPI
|
||||||
|
#define THREAD_RETURN_VALUE 0
|
||||||
|
typedef DWORD (WINAPI *ThreadEntranceFunc)(LPVOID lpThreadParameter);
|
||||||
|
#else
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#define FILE_SEPERATOR "/"
|
||||||
|
typedef int SOCKET;
|
||||||
|
#define closesocket close
|
||||||
|
#define INVALID_SOCKET -1
|
||||||
|
#define THREAD_ENTRANCE_FUNC_DECLARE void *
|
||||||
|
typedef void *LPVOID;
|
||||||
|
#define THREAD_RETURN_VALUE NULL
|
||||||
|
typedef void * (*ThreadEntranceFunc)(LPVOID lpThreadParameter);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef WIN32
|
||||||
|
extern int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int kind);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef OS_LINUX
|
||||||
|
#define PTHREAD_MUTEX_ERRORCHECK PTHREAD_MUTEX_ERRORCHECK_NP
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "_os_bits.h"
|
||||||
|
|
||||||
|
#ifdef OS_BITS
|
||||||
|
#if OS_BITS == 64
|
||||||
|
#define INT64_PRINTF_FORMAT "%ld"
|
||||||
|
#else
|
||||||
|
#define INT64_PRINTF_FORMAT "%lld"
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#define INT64_PRINTF_FORMAT "%lld"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef OFF_BITS
|
||||||
|
#if OFF_BITS == 64
|
||||||
|
#define OFF_PRINTF_FORMAT INT64_PRINTF_FORMAT
|
||||||
|
#else
|
||||||
|
#define OFF_PRINTF_FORMAT "%d"
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#define OFF_PRINTF_FORMAT INT64_PRINTF_FORMAT
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef WIN32
|
||||||
|
#define USE_SENDFILE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define MAX_PATH_SIZE 256
|
||||||
|
#define LOG_FILE_DIR "logs"
|
||||||
|
#define CONF_FILE_DIR "conf"
|
||||||
|
#define DEFAULT_CONNECT_TIMEOUT 30
|
||||||
|
#define DEFAULT_NETWORK_TIMEOUT 30
|
||||||
|
#define DEFAULT_MAX_CONNECTONS 256
|
||||||
|
#define DEFAULT_WORK_THREADS 4
|
||||||
|
#define SYNC_LOG_BUFF_DEF_INTERVAL 10
|
||||||
|
#define TIME_NONE -1
|
||||||
|
|
||||||
|
#define IP_ADDRESS_SIZE 16
|
||||||
|
#define INFINITE_FILE_SIZE (256 * 1024LL * 1024 * 1024 * 1024 * 1024LL)
|
||||||
|
|
||||||
|
#ifndef __cplusplus
|
||||||
|
#ifndef true
|
||||||
|
typedef char bool;
|
||||||
|
#define true 1
|
||||||
|
#define false 0
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef byte
|
||||||
|
#define byte signed char
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef ubyte
|
||||||
|
#define ubyte unsigned char
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef WIN32
|
||||||
|
#ifndef INADDR_NONE
|
||||||
|
#define INADDR_NONE ((in_addr_t) 0xffffffff)
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef ECANCELED
|
||||||
|
#define ECANCELED 125
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef ENONET
|
||||||
|
#define ENONET 64 /* Machine is not on the network */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define IS_UPPER_HEX(ch) ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F'))
|
||||||
|
#define STRERROR(no) (strerror(no) != NULL ? strerror(no) : "Unkown error")
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
byte hour;
|
||||||
|
byte minute;
|
||||||
|
} TimeInfo;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
char major;
|
||||||
|
char minor;
|
||||||
|
} Version;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
char *key;
|
||||||
|
char *value;
|
||||||
|
} KeyValuePair;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
char *buff;
|
||||||
|
int alloc_size;
|
||||||
|
int length;
|
||||||
|
} BufferInfo;
|
||||||
|
|
||||||
|
typedef void (*FreeDataFunc)(void *ptr);
|
||||||
|
typedef int (*CompareFunc)(void *p1, void *p2);
|
||||||
|
typedef void* (*MallocFunc)(size_t size);
|
||||||
|
|
||||||
|
#define TO_UPPERCASE(c) (((c) >= 'a' && (c) <= 'z') ? (c) - 32 : c)
|
||||||
|
#define MEM_ALIGN(x) (((x) + 7) & (~7))
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
#define strcasecmp _stricmp
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,346 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2008 Happy Fish / YuQing
|
||||||
|
*
|
||||||
|
* FastDFS may be copied only under the terms of the GNU General
|
||||||
|
* Public License V3, which may be found in the FastDFS source kit.
|
||||||
|
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||||
|
**/
|
||||||
|
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include "logger.h"
|
||||||
|
#include "sockopt.h"
|
||||||
|
#include "shared_func.h"
|
||||||
|
#include "sched_thread.h"
|
||||||
|
#include "connection_pool.h"
|
||||||
|
|
||||||
|
int conn_pool_init(ConnectionPool *cp, int connect_timeout, \
|
||||||
|
const int max_count_per_entry, const int max_idle_time)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
|
||||||
|
if ((result=init_pthread_lock(&cp->lock)) != 0)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
cp->connect_timeout = connect_timeout;
|
||||||
|
cp->max_count_per_entry = max_count_per_entry;
|
||||||
|
cp->max_idle_time = max_idle_time;
|
||||||
|
|
||||||
|
return hash_init(&(cp->hash_array), simple_hash, 1024, 0.75);
|
||||||
|
}
|
||||||
|
|
||||||
|
void conn_pool_destroy(ConnectionPool *cp)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&cp->lock);
|
||||||
|
hash_destroy(&(cp->hash_array));
|
||||||
|
pthread_mutex_unlock(&cp->lock);
|
||||||
|
|
||||||
|
pthread_mutex_destroy(&cp->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void conn_pool_disconnect_server(ConnectionInfo *pConnection)
|
||||||
|
{
|
||||||
|
if (pConnection->sock >= 0)
|
||||||
|
{
|
||||||
|
close(pConnection->sock);
|
||||||
|
pConnection->sock = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int conn_pool_connect_server(ConnectionInfo *pConnection, \
|
||||||
|
const int connect_timeout)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
|
||||||
|
if (pConnection->sock >= 0)
|
||||||
|
{
|
||||||
|
close(pConnection->sock);
|
||||||
|
}
|
||||||
|
|
||||||
|
pConnection->sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
if(pConnection->sock < 0)
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"socket create failed, errno: %d, " \
|
||||||
|
"error info: %s", __LINE__, errno, STRERROR(errno));
|
||||||
|
return errno != 0 ? errno : EPERM;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((result=tcpsetnonblockopt(pConnection->sock)) != 0)
|
||||||
|
{
|
||||||
|
close(pConnection->sock);
|
||||||
|
pConnection->sock = -1;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((result=connectserverbyip_nb(pConnection->sock, \
|
||||||
|
pConnection->ip_addr, pConnection->port, \
|
||||||
|
connect_timeout)) != 0)
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"connect to %s:%d fail, errno: %d, " \
|
||||||
|
"error info: %s", __LINE__, pConnection->ip_addr, \
|
||||||
|
pConnection->port, result, STRERROR(result));
|
||||||
|
|
||||||
|
close(pConnection->sock);
|
||||||
|
pConnection->sock = -1;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int conn_pool_get_key(const ConnectionInfo *conn, char *key, int *key_len)
|
||||||
|
{
|
||||||
|
struct in_addr sin_addr;
|
||||||
|
|
||||||
|
if (inet_aton(conn->ip_addr, &sin_addr) == 0)
|
||||||
|
{
|
||||||
|
*key_len = 0;
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int2buff(sin_addr.s_addr, key);
|
||||||
|
*key_len = 4 + sprintf(key + 4, "%d", conn->port);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionInfo *conn_pool_get_connection(ConnectionPool *cp,
|
||||||
|
const ConnectionInfo *conn, int *err_no)
|
||||||
|
{
|
||||||
|
char key[32];
|
||||||
|
int key_len;
|
||||||
|
int bytes;
|
||||||
|
char *p;
|
||||||
|
ConnectionManager *cm;
|
||||||
|
ConnectionNode *node;
|
||||||
|
ConnectionInfo *ci;
|
||||||
|
time_t current_time;
|
||||||
|
|
||||||
|
*err_no = conn_pool_get_key(conn, key, &key_len);
|
||||||
|
if (*err_no != 0)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_lock(&cp->lock);
|
||||||
|
cm = (ConnectionManager *)hash_find(&cp->hash_array, key, key_len);
|
||||||
|
if (cm == NULL)
|
||||||
|
{
|
||||||
|
cm = (ConnectionManager *)malloc(sizeof(ConnectionManager));
|
||||||
|
if (cm == NULL)
|
||||||
|
{
|
||||||
|
*err_no = errno != 0 ? errno : ENOMEM;
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"malloc %d bytes fail, errno: %d, " \
|
||||||
|
"error info: %s", __LINE__, \
|
||||||
|
(int)sizeof(ConnectionManager), \
|
||||||
|
*err_no, STRERROR(*err_no));
|
||||||
|
pthread_mutex_unlock(&cp->lock);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
cm->head = NULL;
|
||||||
|
cm->total_count = 0;
|
||||||
|
cm->free_count = 0;
|
||||||
|
if ((*err_no=init_pthread_lock(&cm->lock)) != 0)
|
||||||
|
{
|
||||||
|
pthread_mutex_unlock(&cp->lock);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
hash_insert(&cp->hash_array, key, key_len, cm);
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&cp->lock);
|
||||||
|
|
||||||
|
current_time = get_current_time();
|
||||||
|
pthread_mutex_lock(&cm->lock);
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
if (cm->head == NULL)
|
||||||
|
{
|
||||||
|
if ((cp->max_count_per_entry > 0) &&
|
||||||
|
(cm->total_count >= cp->max_count_per_entry))
|
||||||
|
{
|
||||||
|
*err_no = ENOSPC;
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"connections: %d of server %s:%d " \
|
||||||
|
"exceed limit: %d", __LINE__, \
|
||||||
|
cm->total_count, conn->ip_addr, \
|
||||||
|
conn->port, cp->max_count_per_entry);
|
||||||
|
pthread_mutex_unlock(&cm->lock);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes = sizeof(ConnectionInfo) + sizeof(ConnectionNode);
|
||||||
|
p = (char *)malloc(bytes);
|
||||||
|
if (p == NULL)
|
||||||
|
{
|
||||||
|
*err_no = errno != 0 ? errno : ENOMEM;
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"malloc %d bytes fail, errno: %d, " \
|
||||||
|
"error info: %s", __LINE__, \
|
||||||
|
bytes, *err_no, STRERROR(*err_no));
|
||||||
|
pthread_mutex_unlock(&cm->lock);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
node = (ConnectionNode *)(p + sizeof(ConnectionInfo));
|
||||||
|
node->conn = (ConnectionInfo *)p;
|
||||||
|
node->manager = cm;
|
||||||
|
node->next = NULL;
|
||||||
|
node->atime = 0;
|
||||||
|
|
||||||
|
cm->total_count++;
|
||||||
|
pthread_mutex_unlock(&cm->lock);
|
||||||
|
|
||||||
|
memcpy(node->conn, conn, sizeof(ConnectionInfo));
|
||||||
|
node->conn->sock = -1;
|
||||||
|
*err_no = conn_pool_connect_server(node->conn, \
|
||||||
|
cp->connect_timeout);
|
||||||
|
if (*err_no != 0)
|
||||||
|
{
|
||||||
|
free(p);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
logDebug("file: "__FILE__", line: %d, " \
|
||||||
|
"server %s:%d, new connection: %d, " \
|
||||||
|
"total_count: %d, free_count: %d", \
|
||||||
|
__LINE__, conn->ip_addr, conn->port, \
|
||||||
|
node->conn->sock, cm->total_count, \
|
||||||
|
cm->free_count);
|
||||||
|
return node->conn;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
node = cm->head;
|
||||||
|
ci = node->conn;
|
||||||
|
cm->head = node->next;
|
||||||
|
cm->free_count--;
|
||||||
|
|
||||||
|
if (current_time - node->atime > cp->max_idle_time)
|
||||||
|
{
|
||||||
|
cm->total_count--;
|
||||||
|
|
||||||
|
logDebug("file: "__FILE__", line: %d, " \
|
||||||
|
"server %s:%d, connection: %d idle " \
|
||||||
|
"time: %d exceeds max idle time: %d, "\
|
||||||
|
"total_count: %d, free_count: %d", \
|
||||||
|
__LINE__, conn->ip_addr, conn->port, \
|
||||||
|
ci->sock, \
|
||||||
|
(int)(current_time - node->atime), \
|
||||||
|
cp->max_idle_time, cm->total_count, \
|
||||||
|
cm->free_count);
|
||||||
|
|
||||||
|
conn_pool_disconnect_server(ci);
|
||||||
|
free(ci);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&cm->lock);
|
||||||
|
logDebug("file: "__FILE__", line: %d, " \
|
||||||
|
"server %s:%d, reuse connection: %d, " \
|
||||||
|
"total_count: %d, free_count: %d",
|
||||||
|
__LINE__, conn->ip_addr, conn->port,
|
||||||
|
ci->sock, cm->total_count, cm->free_count);
|
||||||
|
return ci;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int conn_pool_close_connection_ex(ConnectionPool *cp, ConnectionInfo *conn,
|
||||||
|
const bool bForce)
|
||||||
|
{
|
||||||
|
char key[32];
|
||||||
|
int result;
|
||||||
|
int key_len;
|
||||||
|
ConnectionManager *cm;
|
||||||
|
ConnectionNode *node;
|
||||||
|
|
||||||
|
result = conn_pool_get_key(conn, key, &key_len);
|
||||||
|
if (result != 0)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_lock(&cp->lock);
|
||||||
|
cm = (ConnectionManager *)hash_find(&cp->hash_array, key, key_len);
|
||||||
|
pthread_mutex_unlock(&cp->lock);
|
||||||
|
if (cm == NULL)
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"hash entry of server %s:%d not exist", __LINE__, \
|
||||||
|
conn->ip_addr, conn->port);
|
||||||
|
return ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
node = (ConnectionNode *)(((char *)conn) + sizeof(ConnectionInfo));
|
||||||
|
if (node->manager != cm)
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"manager of server entry %s:%d is invalid!", \
|
||||||
|
__LINE__, conn->ip_addr, conn->port);
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_lock(&cm->lock);
|
||||||
|
if (bForce)
|
||||||
|
{
|
||||||
|
cm->total_count--;
|
||||||
|
|
||||||
|
logDebug("file: "__FILE__", line: %d, " \
|
||||||
|
"server %s:%d, release connection: %d, " \
|
||||||
|
"total_count: %d, free_count: %d",
|
||||||
|
__LINE__, conn->ip_addr, conn->port,
|
||||||
|
conn->sock, cm->total_count, cm->free_count);
|
||||||
|
|
||||||
|
conn_pool_disconnect_server(conn);
|
||||||
|
free(conn);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
node->atime = get_current_time();
|
||||||
|
node->next = cm->head;
|
||||||
|
cm->head = node;
|
||||||
|
cm->free_count++;
|
||||||
|
|
||||||
|
logDebug("file: "__FILE__", line: %d, " \
|
||||||
|
"server %s:%d, free connection: %d, " \
|
||||||
|
"total_count: %d, free_count: %d",
|
||||||
|
__LINE__, conn->ip_addr, conn->port,
|
||||||
|
conn->sock, cm->total_count, cm->free_count);
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&cm->lock);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int _conn_count_walk(const int index, const HashData *data, void *args)
|
||||||
|
{
|
||||||
|
int *count;
|
||||||
|
ConnectionManager *cm;
|
||||||
|
ConnectionNode *node;
|
||||||
|
|
||||||
|
count = (int *)args;
|
||||||
|
cm = (ConnectionManager *)data->value;
|
||||||
|
node = cm->head;
|
||||||
|
while (node != NULL)
|
||||||
|
{
|
||||||
|
(*count)++;
|
||||||
|
node = node->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int conn_pool_get_connection_count(ConnectionPool *cp)
|
||||||
|
{
|
||||||
|
int count;
|
||||||
|
count = 0;
|
||||||
|
hash_walk(&cp->hash_array, _conn_count_walk, &count);
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,86 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2008 Happy Fish / YuQing
|
||||||
|
*
|
||||||
|
* FastDFS may be copied only under the terms of the GNU General
|
||||||
|
* Public License V3, which may be found in the FastDFS source kit.
|
||||||
|
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||||
|
**/
|
||||||
|
|
||||||
|
//connection_pool.h
|
||||||
|
|
||||||
|
#ifndef _CONNECTION_POOL_H
|
||||||
|
#define _CONNECTION_POOL_H
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include "common_define.h"
|
||||||
|
#include "pthread_func.h"
|
||||||
|
#include "hash.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int sock;
|
||||||
|
int port;
|
||||||
|
char ip_addr[IP_ADDRESS_SIZE];
|
||||||
|
} ConnectionInfo;
|
||||||
|
|
||||||
|
struct tagConnectionManager;
|
||||||
|
|
||||||
|
typedef struct tagConnectionNode {
|
||||||
|
ConnectionInfo *conn;
|
||||||
|
struct tagConnectionManager *manager;
|
||||||
|
struct tagConnectionNode *next;
|
||||||
|
time_t atime; //last access time
|
||||||
|
} ConnectionNode;
|
||||||
|
|
||||||
|
typedef struct tagConnectionManager {
|
||||||
|
ConnectionNode *head;
|
||||||
|
int total_count; //total connections
|
||||||
|
int free_count; //free connections
|
||||||
|
pthread_mutex_t lock;
|
||||||
|
} ConnectionManager;
|
||||||
|
|
||||||
|
typedef struct tagConnectionPool {
|
||||||
|
HashArray hash_array; //key is ip:port, value is ConnectionManager
|
||||||
|
pthread_mutex_t lock;
|
||||||
|
int connect_timeout;
|
||||||
|
int max_count_per_entry; //0 means no limit
|
||||||
|
|
||||||
|
/*
|
||||||
|
connections whose the idle time exceeds this time will be closed
|
||||||
|
*/
|
||||||
|
int max_idle_time;
|
||||||
|
} ConnectionPool;
|
||||||
|
|
||||||
|
int conn_pool_init(ConnectionPool *cp, int connect_timeout, \
|
||||||
|
const int max_count_per_entry, const int max_idle_time);
|
||||||
|
void conn_pool_destroy(ConnectionPool *cp);
|
||||||
|
|
||||||
|
ConnectionInfo *conn_pool_get_connection(ConnectionPool *cp,
|
||||||
|
const ConnectionInfo *conn, int *err_no);
|
||||||
|
|
||||||
|
#define conn_pool_close_connection(cp, conn) \
|
||||||
|
conn_pool_close_connection_ex(cp, conn, false)
|
||||||
|
|
||||||
|
int conn_pool_close_connection_ex(ConnectionPool *cp, ConnectionInfo *conn,
|
||||||
|
const bool bForce);
|
||||||
|
|
||||||
|
void conn_pool_disconnect_server(ConnectionInfo *pConnection);
|
||||||
|
|
||||||
|
int conn_pool_connect_server(ConnectionInfo *pConnection, \
|
||||||
|
const int connect_timeout);
|
||||||
|
|
||||||
|
int conn_pool_get_connection_count(ConnectionPool *cp);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
@ -0,0 +1,225 @@
|
||||||
|
//fast_mblock.c
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/resource.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include "fast_mblock.h"
|
||||||
|
#include "logger.h"
|
||||||
|
#include "shared_func.h"
|
||||||
|
#include "pthread_func.h"
|
||||||
|
|
||||||
|
int fast_mblock_init(struct fast_mblock_man *mblock, const int element_size, \
|
||||||
|
const int alloc_elements_once)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
|
||||||
|
if (element_size <= 0)
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"invalid block size: %d", \
|
||||||
|
__LINE__, element_size);
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
mblock->element_size = element_size;
|
||||||
|
if (alloc_elements_once > 0)
|
||||||
|
{
|
||||||
|
mblock->alloc_elements_once = alloc_elements_once;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int block_size;
|
||||||
|
block_size = MEM_ALIGN(sizeof(struct fast_mblock_node) \
|
||||||
|
+ element_size);
|
||||||
|
mblock->alloc_elements_once = (1024 * 1024) / block_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((result=init_pthread_lock(&(mblock->lock))) != 0)
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"init_pthread_lock fail, errno: %d, error info: %s", \
|
||||||
|
__LINE__, result, STRERROR(result));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
mblock->malloc_chain_head = NULL;
|
||||||
|
mblock->free_chain_head = NULL;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fast_mblock_prealloc(struct fast_mblock_man *mblock)
|
||||||
|
{
|
||||||
|
struct fast_mblock_node *pNode;
|
||||||
|
struct fast_mblock_malloc *pMallocNode;
|
||||||
|
char *pNew;
|
||||||
|
char *pTrunkStart;
|
||||||
|
char *p;
|
||||||
|
char *pLast;
|
||||||
|
int block_size;
|
||||||
|
int alloc_size;
|
||||||
|
|
||||||
|
block_size = MEM_ALIGN(sizeof(struct fast_mblock_node) + \
|
||||||
|
mblock->element_size);
|
||||||
|
alloc_size = sizeof(struct fast_mblock_malloc) + block_size * \
|
||||||
|
mblock->alloc_elements_once;
|
||||||
|
|
||||||
|
pNew = (char *)malloc(alloc_size);
|
||||||
|
if (pNew == NULL)
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"malloc %d bytes fail, " \
|
||||||
|
"errno: %d, error info: %s", \
|
||||||
|
__LINE__, alloc_size, errno, STRERROR(errno));
|
||||||
|
return errno != 0 ? errno : ENOMEM;
|
||||||
|
}
|
||||||
|
memset(pNew, 0, alloc_size);
|
||||||
|
|
||||||
|
pMallocNode = (struct fast_mblock_malloc *)pNew;
|
||||||
|
|
||||||
|
pTrunkStart = pNew + sizeof(struct fast_mblock_malloc);
|
||||||
|
pLast = pNew + (alloc_size - block_size);
|
||||||
|
for (p=pTrunkStart; p<pLast; p += block_size)
|
||||||
|
{
|
||||||
|
pNode = (struct fast_mblock_node *)p;
|
||||||
|
pNode->next = (struct fast_mblock_node *)(p + block_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
((struct fast_mblock_node *)pLast)->next = NULL;
|
||||||
|
mblock->free_chain_head = (struct fast_mblock_node *)pTrunkStart;
|
||||||
|
|
||||||
|
pMallocNode->next = mblock->malloc_chain_head;
|
||||||
|
mblock->malloc_chain_head = pMallocNode;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fast_mblock_destroy(struct fast_mblock_man *mblock)
|
||||||
|
{
|
||||||
|
struct fast_mblock_malloc *pMallocNode;
|
||||||
|
struct fast_mblock_malloc *pMallocTmp;
|
||||||
|
|
||||||
|
if (mblock->malloc_chain_head == NULL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pMallocNode = mblock->malloc_chain_head;
|
||||||
|
while (pMallocNode != NULL)
|
||||||
|
{
|
||||||
|
pMallocTmp = pMallocNode;
|
||||||
|
pMallocNode = pMallocNode->next;
|
||||||
|
|
||||||
|
free(pMallocTmp);
|
||||||
|
}
|
||||||
|
mblock->malloc_chain_head = NULL;
|
||||||
|
mblock->free_chain_head = NULL;
|
||||||
|
|
||||||
|
pthread_mutex_destroy(&(mblock->lock));
|
||||||
|
}
|
||||||
|
|
||||||
|
struct fast_mblock_node *fast_mblock_alloc(struct fast_mblock_man *mblock)
|
||||||
|
{
|
||||||
|
struct fast_mblock_node *pNode;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
if ((result=pthread_mutex_lock(&(mblock->lock))) != 0)
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"call pthread_mutex_lock fail, " \
|
||||||
|
"errno: %d, error info: %s", \
|
||||||
|
__LINE__, result, STRERROR(result));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mblock->free_chain_head != NULL)
|
||||||
|
{
|
||||||
|
pNode = mblock->free_chain_head;
|
||||||
|
mblock->free_chain_head = pNode->next;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ((result=fast_mblock_prealloc(mblock)) == 0)
|
||||||
|
{
|
||||||
|
pNode = mblock->free_chain_head;
|
||||||
|
mblock->free_chain_head = pNode->next;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pNode = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((result=pthread_mutex_unlock(&(mblock->lock))) != 0)
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"call pthread_mutex_unlock fail, " \
|
||||||
|
"errno: %d, error info: %s", \
|
||||||
|
__LINE__, result, STRERROR(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
return pNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fast_mblock_free(struct fast_mblock_man *mblock, \
|
||||||
|
struct fast_mblock_node *pNode)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
|
||||||
|
if ((result=pthread_mutex_lock(&(mblock->lock))) != 0)
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"call pthread_mutex_lock fail, " \
|
||||||
|
"errno: %d, error info: %s", \
|
||||||
|
__LINE__, result, STRERROR(result));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
pNode->next = mblock->free_chain_head;
|
||||||
|
mblock->free_chain_head = pNode;
|
||||||
|
|
||||||
|
if ((result=pthread_mutex_unlock(&(mblock->lock))) != 0)
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"call pthread_mutex_unlock fail, " \
|
||||||
|
"errno: %d, error info: %s", \
|
||||||
|
__LINE__, result, STRERROR(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fast_mblock_count(struct fast_mblock_man *mblock)
|
||||||
|
{
|
||||||
|
struct fast_mblock_node *pNode;
|
||||||
|
int count;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
if ((result=pthread_mutex_lock(&(mblock->lock))) != 0)
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"call pthread_mutex_lock fail, " \
|
||||||
|
"errno: %d, error info: %s", \
|
||||||
|
__LINE__, result, STRERROR(result));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
count = 0;
|
||||||
|
pNode = mblock->free_chain_head;
|
||||||
|
while (pNode != NULL)
|
||||||
|
{
|
||||||
|
pNode = pNode->next;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((result=pthread_mutex_unlock(&(mblock->lock))) != 0)
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"call pthread_mutex_unlock fail, " \
|
||||||
|
"errno: %d, error info: %s", \
|
||||||
|
__LINE__, result, STRERROR(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,100 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2008 Happy Fish / YuQing
|
||||||
|
*
|
||||||
|
* FastDFS may be copied only under the terms of the GNU General
|
||||||
|
* Public License V3, which may be found in the FastDFS source kit.
|
||||||
|
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||||
|
**/
|
||||||
|
|
||||||
|
//fast_mblock.h
|
||||||
|
|
||||||
|
#ifndef _FAST_MBLOCK_H
|
||||||
|
#define _FAST_MBLOCK_H
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include "common_define.h"
|
||||||
|
#include "chain.h"
|
||||||
|
|
||||||
|
/* free node chain */
|
||||||
|
struct fast_mblock_node
|
||||||
|
{
|
||||||
|
struct fast_mblock_node *next;
|
||||||
|
char data[0]; //the data buffer
|
||||||
|
};
|
||||||
|
|
||||||
|
/* malloc chain */
|
||||||
|
struct fast_mblock_malloc
|
||||||
|
{
|
||||||
|
struct fast_mblock_malloc *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct fast_mblock_man
|
||||||
|
{
|
||||||
|
struct fast_mblock_node *free_chain_head; //free node chain
|
||||||
|
struct fast_mblock_malloc *malloc_chain_head; //malloc chain to be freed
|
||||||
|
int element_size; //element size
|
||||||
|
int alloc_elements_once; //alloc elements once
|
||||||
|
pthread_mutex_t lock; //the lock for read / write free node chain
|
||||||
|
};
|
||||||
|
|
||||||
|
#define fast_mblock_to_node_ptr(data_ptr) \
|
||||||
|
(struct fast_mblock_node *)(data_ptr - ((size_t)(char *) \
|
||||||
|
&((struct fast_mblock_node *)0)->data))
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
mblock init
|
||||||
|
parameters:
|
||||||
|
mblock: the mblock pointer
|
||||||
|
element_size: element size, such as sizeof(struct xxx)
|
||||||
|
alloc_elements_once: malloc elements once, 0 for malloc 1MB once
|
||||||
|
return error no, 0 for success, != 0 fail
|
||||||
|
*/
|
||||||
|
int fast_mblock_init(struct fast_mblock_man *mblock, const int element_size, \
|
||||||
|
const int alloc_elements_once);
|
||||||
|
|
||||||
|
/**
|
||||||
|
mblock destroy
|
||||||
|
parameters:
|
||||||
|
mblock: the mblock pointer
|
||||||
|
*/
|
||||||
|
void fast_mblock_destroy(struct fast_mblock_man *mblock);
|
||||||
|
|
||||||
|
/**
|
||||||
|
alloc a node from the mblock
|
||||||
|
parameters:
|
||||||
|
mblock: the mblock pointer
|
||||||
|
return the alloced node, return NULL if fail
|
||||||
|
*/
|
||||||
|
struct fast_mblock_node *fast_mblock_alloc(struct fast_mblock_man *mblock);
|
||||||
|
|
||||||
|
/**
|
||||||
|
free a node (put a node to the mblock)
|
||||||
|
parameters:
|
||||||
|
mblock: the mblock pointer
|
||||||
|
pNode: the node to free
|
||||||
|
return the alloced node, return NULL if fail
|
||||||
|
*/
|
||||||
|
int fast_mblock_free(struct fast_mblock_man *mblock, \
|
||||||
|
struct fast_mblock_node *pNode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
get node count of the mblock
|
||||||
|
parameters:
|
||||||
|
mblock: the mblock pointer
|
||||||
|
return the free node count of the mblock, return -1 if fail
|
||||||
|
*/
|
||||||
|
int fast_mblock_count(struct fast_mblock_man *mblock);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
@ -0,0 +1,503 @@
|
||||||
|
//fast_task_queue.c
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/resource.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include "fast_task_queue.h"
|
||||||
|
#include "logger.h"
|
||||||
|
#include "shared_func.h"
|
||||||
|
#include "pthread_func.h"
|
||||||
|
|
||||||
|
static struct fast_task_queue g_free_queue;
|
||||||
|
|
||||||
|
struct mpool_chain {
|
||||||
|
struct fast_task_info *blocks;
|
||||||
|
struct fast_task_info *last_block; //last block
|
||||||
|
struct mpool_chain *next;
|
||||||
|
} *g_mpool = NULL;
|
||||||
|
|
||||||
|
#define ALIGNED_TASK_INFO_SIZE MEM_ALIGN(sizeof(struct fast_task_info))
|
||||||
|
|
||||||
|
int task_queue_init(struct fast_task_queue *pQueue)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
|
||||||
|
if ((result=init_pthread_lock(&(pQueue->lock))) != 0)
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"init_pthread_lock fail, errno: %d, error info: %s", \
|
||||||
|
__LINE__, result, STRERROR(result));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
pQueue->head = NULL;
|
||||||
|
pQueue->tail = NULL;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct mpool_chain *malloc_mpool(const int block_size, \
|
||||||
|
const int total_alloc_size)
|
||||||
|
{
|
||||||
|
struct fast_task_info *pTask;
|
||||||
|
char *p;
|
||||||
|
char *pCharEnd;
|
||||||
|
struct mpool_chain *mpool;
|
||||||
|
|
||||||
|
mpool = (struct mpool_chain *)malloc(sizeof(struct mpool_chain));
|
||||||
|
if (mpool == NULL)
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"malloc %d bytes fail, " \
|
||||||
|
"errno: %d, error info: %s", \
|
||||||
|
__LINE__, (int)sizeof(struct mpool_chain), \
|
||||||
|
errno, STRERROR(errno));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
mpool->next = NULL;
|
||||||
|
mpool->blocks = (struct fast_task_info *)malloc(total_alloc_size);
|
||||||
|
if (mpool->blocks == NULL)
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"malloc %d bytes fail, " \
|
||||||
|
"errno: %d, error info: %s", \
|
||||||
|
__LINE__, total_alloc_size, \
|
||||||
|
errno, STRERROR(errno));
|
||||||
|
free(mpool);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
memset(mpool->blocks, 0, total_alloc_size);
|
||||||
|
|
||||||
|
pCharEnd = ((char *)mpool->blocks) + total_alloc_size;
|
||||||
|
for (p=(char *)mpool->blocks; p<pCharEnd; p += block_size)
|
||||||
|
{
|
||||||
|
pTask = (struct fast_task_info *)p;
|
||||||
|
pTask->size = g_free_queue.min_buff_size;
|
||||||
|
|
||||||
|
pTask->arg = p + ALIGNED_TASK_INFO_SIZE;
|
||||||
|
if (g_free_queue.malloc_whole_block)
|
||||||
|
{
|
||||||
|
pTask->data = (char *)pTask->arg + \
|
||||||
|
g_free_queue.arg_size;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pTask->data = (char *)malloc(pTask->size);
|
||||||
|
if (pTask->data == NULL)
|
||||||
|
{
|
||||||
|
char *pt;
|
||||||
|
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"malloc %d bytes fail, " \
|
||||||
|
"errno: %d, error info: %s", \
|
||||||
|
__LINE__, pTask->size, \
|
||||||
|
errno, STRERROR(errno));
|
||||||
|
|
||||||
|
for (pt=(char *)mpool->blocks; pt < p; \
|
||||||
|
pt += block_size)
|
||||||
|
{
|
||||||
|
free(((struct fast_task_info *)pt)->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(mpool->blocks);
|
||||||
|
free(mpool);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mpool->last_block = (struct fast_task_info *)(pCharEnd - block_size);
|
||||||
|
for (p=(char *)mpool->blocks; p<(char *)mpool->last_block; p += block_size)
|
||||||
|
{
|
||||||
|
pTask = (struct fast_task_info *)p;
|
||||||
|
pTask->next = (struct fast_task_info *)(p + block_size);
|
||||||
|
}
|
||||||
|
mpool->last_block->next = NULL;
|
||||||
|
|
||||||
|
return mpool;
|
||||||
|
}
|
||||||
|
|
||||||
|
int free_queue_init(const int max_connections, const int min_buff_size, \
|
||||||
|
const int max_buff_size, const int arg_size)
|
||||||
|
{
|
||||||
|
int64_t total_size;
|
||||||
|
struct mpool_chain *mpool;
|
||||||
|
int block_size;
|
||||||
|
int alloc_size;
|
||||||
|
int result;
|
||||||
|
int loop_count;
|
||||||
|
int aligned_min_size;
|
||||||
|
int aligned_max_size;
|
||||||
|
int aligned_arg_size;
|
||||||
|
rlim_t max_data_size;
|
||||||
|
|
||||||
|
if ((result=init_pthread_lock(&(g_free_queue.lock))) != 0)
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"init_pthread_lock fail, errno: %d, error info: %s", \
|
||||||
|
__LINE__, result, STRERROR(result));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
aligned_min_size = MEM_ALIGN(min_buff_size);
|
||||||
|
aligned_max_size = MEM_ALIGN(max_buff_size);
|
||||||
|
aligned_arg_size = MEM_ALIGN(arg_size);
|
||||||
|
block_size = ALIGNED_TASK_INFO_SIZE + aligned_arg_size;
|
||||||
|
alloc_size = block_size * max_connections;
|
||||||
|
if (aligned_max_size > aligned_min_size)
|
||||||
|
{
|
||||||
|
total_size = alloc_size;
|
||||||
|
g_free_queue.malloc_whole_block = false;
|
||||||
|
max_data_size = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
struct rlimit rlimit_data;
|
||||||
|
|
||||||
|
if (getrlimit(RLIMIT_DATA, &rlimit_data) < 0)
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"call getrlimit fail, " \
|
||||||
|
"errno: %d, error info: %s", \
|
||||||
|
__LINE__, errno, STRERROR(errno));
|
||||||
|
return errno != 0 ? errno : EPERM;
|
||||||
|
}
|
||||||
|
if (rlimit_data.rlim_cur == RLIM_INFINITY)
|
||||||
|
{
|
||||||
|
max_data_size = 256 * 1024 * 1024;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
max_data_size = rlimit_data.rlim_cur;
|
||||||
|
if (max_data_size > 256 * 1024 * 1024)
|
||||||
|
{
|
||||||
|
max_data_size = 256 * 1024 * 1024;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (max_data_size >= (int64_t)(block_size + aligned_min_size) *
|
||||||
|
(int64_t)max_connections)
|
||||||
|
{
|
||||||
|
total_size = alloc_size + (int64_t)aligned_min_size *
|
||||||
|
max_connections;
|
||||||
|
g_free_queue.malloc_whole_block = true;
|
||||||
|
block_size += aligned_min_size;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
total_size = alloc_size;
|
||||||
|
g_free_queue.malloc_whole_block = false;
|
||||||
|
max_data_size = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logDebug("file: "__FILE__", line: %d, " \
|
||||||
|
"max_connections: %d, min_buff_size: %d, max_buff_size: %d, " \
|
||||||
|
"block_size: %d, arg_size: %d, max_data_size: %d, " \
|
||||||
|
"total_size: "INT64_PRINTF_FORMAT, __LINE__, \
|
||||||
|
max_connections, aligned_min_size, aligned_max_size, \
|
||||||
|
block_size, aligned_arg_size, (int)max_data_size, total_size);
|
||||||
|
|
||||||
|
g_free_queue.max_connections = max_connections;
|
||||||
|
g_free_queue.min_buff_size = aligned_min_size;
|
||||||
|
g_free_queue.max_buff_size = aligned_max_size;
|
||||||
|
g_free_queue.arg_size = aligned_arg_size;
|
||||||
|
|
||||||
|
if ((!g_free_queue.malloc_whole_block) || \
|
||||||
|
(total_size <= max_data_size))
|
||||||
|
{
|
||||||
|
loop_count = 1;
|
||||||
|
mpool = malloc_mpool(block_size, total_size);
|
||||||
|
if (mpool == NULL)
|
||||||
|
{
|
||||||
|
return errno != 0 ? errno : ENOMEM;
|
||||||
|
}
|
||||||
|
g_mpool = mpool;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
struct mpool_chain *previous_mpool;
|
||||||
|
int remain_count;
|
||||||
|
int alloc_once;
|
||||||
|
int current_count;
|
||||||
|
int current_alloc_size;
|
||||||
|
|
||||||
|
mpool = NULL;
|
||||||
|
previous_mpool = NULL;
|
||||||
|
loop_count = 0;
|
||||||
|
remain_count = max_connections;
|
||||||
|
alloc_once = max_data_size / block_size;
|
||||||
|
while (remain_count > 0)
|
||||||
|
{
|
||||||
|
current_count = (remain_count > alloc_once) ? \
|
||||||
|
alloc_once : remain_count;
|
||||||
|
current_alloc_size = block_size * current_count;
|
||||||
|
mpool = malloc_mpool(block_size, current_alloc_size);
|
||||||
|
if (mpool == NULL)
|
||||||
|
{
|
||||||
|
free_queue_destroy();
|
||||||
|
return errno != 0 ? errno : ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (previous_mpool == NULL)
|
||||||
|
{
|
||||||
|
g_mpool = mpool;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
previous_mpool->next = mpool;
|
||||||
|
previous_mpool->last_block->next = mpool->blocks;
|
||||||
|
}
|
||||||
|
previous_mpool = mpool;
|
||||||
|
|
||||||
|
remain_count -= current_count;
|
||||||
|
loop_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
logDebug("file: "__FILE__", line: %d, " \
|
||||||
|
"alloc_once: %d", __LINE__, alloc_once);
|
||||||
|
}
|
||||||
|
|
||||||
|
logDebug("file: "__FILE__", line: %d, " \
|
||||||
|
"malloc task info as whole: %d, malloc loop count: %d", \
|
||||||
|
__LINE__, g_free_queue.malloc_whole_block, loop_count);
|
||||||
|
|
||||||
|
if (g_mpool != NULL)
|
||||||
|
{
|
||||||
|
g_free_queue.head = g_mpool->blocks;
|
||||||
|
g_free_queue.tail = mpool->last_block;
|
||||||
|
|
||||||
|
/*
|
||||||
|
struct fast_task_info *pTask;
|
||||||
|
int task_count = 0;
|
||||||
|
|
||||||
|
pTask = g_free_queue.head;
|
||||||
|
while (pTask != NULL)
|
||||||
|
{
|
||||||
|
task_count++;
|
||||||
|
pTask = pTask->next;
|
||||||
|
}
|
||||||
|
logDebug("file: "__FILE__", line: %d, " \
|
||||||
|
"task count: %d", __LINE__, task_count);
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_queue_destroy()
|
||||||
|
{
|
||||||
|
struct mpool_chain *mpool;
|
||||||
|
struct mpool_chain *mp;
|
||||||
|
|
||||||
|
if (g_mpool == NULL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!g_free_queue.malloc_whole_block)
|
||||||
|
{
|
||||||
|
char *p;
|
||||||
|
char *pCharEnd;
|
||||||
|
int block_size;
|
||||||
|
struct fast_task_info *pTask;
|
||||||
|
|
||||||
|
block_size = ALIGNED_TASK_INFO_SIZE + g_free_queue.arg_size;
|
||||||
|
pCharEnd = ((char *)g_mpool->blocks) + block_size * \
|
||||||
|
g_free_queue.max_connections;
|
||||||
|
for (p=(char *)g_mpool->blocks; p<pCharEnd; p += block_size)
|
||||||
|
{
|
||||||
|
pTask = (struct fast_task_info *)p;
|
||||||
|
if (pTask->data != NULL)
|
||||||
|
{
|
||||||
|
free(pTask->data);
|
||||||
|
pTask->data = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mpool = g_mpool;
|
||||||
|
while (mpool != NULL)
|
||||||
|
{
|
||||||
|
mp = mpool;
|
||||||
|
mpool = mpool->next;
|
||||||
|
|
||||||
|
free(mp->blocks);
|
||||||
|
free(mp);
|
||||||
|
}
|
||||||
|
g_mpool = NULL;
|
||||||
|
|
||||||
|
pthread_mutex_destroy(&(g_free_queue.lock));
|
||||||
|
}
|
||||||
|
|
||||||
|
struct fast_task_info *free_queue_pop()
|
||||||
|
{
|
||||||
|
return task_queue_pop(&g_free_queue);;
|
||||||
|
}
|
||||||
|
|
||||||
|
int free_queue_push(struct fast_task_info *pTask)
|
||||||
|
{
|
||||||
|
char *new_buff;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
*(pTask->client_ip) = '\0';
|
||||||
|
pTask->length = 0;
|
||||||
|
pTask->offset = 0;
|
||||||
|
pTask->req_count = 0;
|
||||||
|
|
||||||
|
if (pTask->size > g_free_queue.min_buff_size) //need thrink
|
||||||
|
{
|
||||||
|
new_buff = (char *)malloc(g_free_queue.min_buff_size);
|
||||||
|
if (new_buff == NULL)
|
||||||
|
{
|
||||||
|
logWarning("file: "__FILE__", line: %d, " \
|
||||||
|
"malloc %d bytes fail, " \
|
||||||
|
"errno: %d, error info: %s", \
|
||||||
|
__LINE__, g_free_queue.min_buff_size, \
|
||||||
|
errno, STRERROR(errno));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
free(pTask->data);
|
||||||
|
pTask->size = g_free_queue.min_buff_size;
|
||||||
|
pTask->data = new_buff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((result=pthread_mutex_lock(&g_free_queue.lock)) != 0)
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"call pthread_mutex_lock fail, " \
|
||||||
|
"errno: %d, error info: %s", \
|
||||||
|
__LINE__, result, STRERROR(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
pTask->next = g_free_queue.head;
|
||||||
|
g_free_queue.head = pTask;
|
||||||
|
if (g_free_queue.tail == NULL)
|
||||||
|
{
|
||||||
|
g_free_queue.tail = pTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((result=pthread_mutex_unlock(&g_free_queue.lock)) != 0)
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"call pthread_mutex_unlock fail, " \
|
||||||
|
"errno: %d, error info: %s", \
|
||||||
|
__LINE__, result, STRERROR(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int free_queue_count()
|
||||||
|
{
|
||||||
|
return task_queue_count(&g_free_queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
int task_queue_push(struct fast_task_queue *pQueue, \
|
||||||
|
struct fast_task_info *pTask)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
|
||||||
|
if ((result=pthread_mutex_lock(&(pQueue->lock))) != 0)
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"call pthread_mutex_lock fail, " \
|
||||||
|
"errno: %d, error info: %s", \
|
||||||
|
__LINE__, result, STRERROR(result));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
pTask->next = NULL;
|
||||||
|
if (pQueue->tail == NULL)
|
||||||
|
{
|
||||||
|
pQueue->head = pTask;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pQueue->tail->next = pTask;
|
||||||
|
}
|
||||||
|
pQueue->tail = pTask;
|
||||||
|
|
||||||
|
if ((result=pthread_mutex_unlock(&(pQueue->lock))) != 0)
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"call pthread_mutex_unlock fail, " \
|
||||||
|
"errno: %d, error info: %s", \
|
||||||
|
__LINE__, result, STRERROR(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct fast_task_info *task_queue_pop(struct fast_task_queue *pQueue)
|
||||||
|
{
|
||||||
|
struct fast_task_info *pTask;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
if ((result=pthread_mutex_lock(&(pQueue->lock))) != 0)
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"call pthread_mutex_lock fail, " \
|
||||||
|
"errno: %d, error info: %s", \
|
||||||
|
__LINE__, result, STRERROR(result));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
pTask = pQueue->head;
|
||||||
|
if (pTask != NULL)
|
||||||
|
{
|
||||||
|
pQueue->head = pTask->next;
|
||||||
|
if (pQueue->head == NULL)
|
||||||
|
{
|
||||||
|
pQueue->tail = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((result=pthread_mutex_unlock(&(pQueue->lock))) != 0)
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"call pthread_mutex_unlock fail, " \
|
||||||
|
"errno: %d, error info: %s", \
|
||||||
|
__LINE__, result, STRERROR(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
return pTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
int task_queue_count(struct fast_task_queue *pQueue)
|
||||||
|
{
|
||||||
|
struct fast_task_info *pTask;
|
||||||
|
int count;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
if ((result=pthread_mutex_lock(&(pQueue->lock))) != 0)
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"call pthread_mutex_lock fail, " \
|
||||||
|
"errno: %d, error info: %s", \
|
||||||
|
__LINE__, result, STRERROR(result));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
count = 0;
|
||||||
|
pTask = pQueue->head;
|
||||||
|
while (pTask != NULL)
|
||||||
|
{
|
||||||
|
pTask = pTask->next;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((result=pthread_mutex_unlock(&(pQueue->lock))) != 0)
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"call pthread_mutex_unlock fail, " \
|
||||||
|
"errno: %d, error info: %s", \
|
||||||
|
__LINE__, result, STRERROR(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,96 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2008 Happy Fish / YuQing
|
||||||
|
*
|
||||||
|
* FastDFS may be copied only under the terms of the GNU General
|
||||||
|
* Public License V3, which may be found in the FastDFS source kit.
|
||||||
|
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||||
|
**/
|
||||||
|
|
||||||
|
//fast_task_queue.h
|
||||||
|
|
||||||
|
#ifndef _FAST_TASK_QUEUE_H
|
||||||
|
#define _FAST_TASK_QUEUE_H
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include "common_define.h"
|
||||||
|
#include "ioevent.h"
|
||||||
|
#include "fast_timer.h"
|
||||||
|
|
||||||
|
struct fast_task_info;
|
||||||
|
|
||||||
|
typedef int (*TaskFinishCallBack) (struct fast_task_info *pTask);
|
||||||
|
typedef void (*TaskCleanUpCallBack) (struct fast_task_info *pTask);
|
||||||
|
|
||||||
|
typedef void (*IOEventCallback) (int sock, short event, void *arg);
|
||||||
|
|
||||||
|
typedef struct ioevent_entry
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
FastTimerEntry timer;
|
||||||
|
IOEventCallback callback;
|
||||||
|
} IOEventEntry;
|
||||||
|
|
||||||
|
struct nio_thread_data
|
||||||
|
{
|
||||||
|
struct ioevent_puller ev_puller;
|
||||||
|
struct fast_timer timer;
|
||||||
|
int pipe_fds[2];
|
||||||
|
struct fast_task_info *deleted_list;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct fast_task_info
|
||||||
|
{
|
||||||
|
IOEventEntry event;
|
||||||
|
char client_ip[IP_ADDRESS_SIZE];
|
||||||
|
void *arg; //extra argument pointer
|
||||||
|
char *data; //buffer for write or recv
|
||||||
|
int size; //alloc size
|
||||||
|
int length; //data length
|
||||||
|
int offset; //current offset
|
||||||
|
int req_count; //request count
|
||||||
|
TaskFinishCallBack finish_callback;
|
||||||
|
struct nio_thread_data *thread_data;
|
||||||
|
struct fast_task_info *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct fast_task_queue
|
||||||
|
{
|
||||||
|
struct fast_task_info *head;
|
||||||
|
struct fast_task_info *tail;
|
||||||
|
pthread_mutex_t lock;
|
||||||
|
int max_connections;
|
||||||
|
int min_buff_size;
|
||||||
|
int max_buff_size;
|
||||||
|
int arg_size;
|
||||||
|
bool malloc_whole_block;
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int free_queue_init(const int max_connections, const int min_buff_size, \
|
||||||
|
const int max_buff_size, const int arg_size);
|
||||||
|
|
||||||
|
void free_queue_destroy();
|
||||||
|
|
||||||
|
int free_queue_push(struct fast_task_info *pTask);
|
||||||
|
struct fast_task_info *free_queue_pop();
|
||||||
|
int free_queue_count();
|
||||||
|
|
||||||
|
|
||||||
|
int task_queue_init(struct fast_task_queue *pQueue);
|
||||||
|
int task_queue_push(struct fast_task_queue *pQueue, \
|
||||||
|
struct fast_task_info *pTask);
|
||||||
|
struct fast_task_info *task_queue_pop(struct fast_task_queue *pQueue);
|
||||||
|
int task_queue_count(struct fast_task_queue *pQueue);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
@ -0,0 +1,175 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include "logger.h"
|
||||||
|
#include "fast_timer.h"
|
||||||
|
|
||||||
|
int fast_timer_init(FastTimer *timer, const int slot_count,
|
||||||
|
const int64_t current_time)
|
||||||
|
{
|
||||||
|
int bytes;
|
||||||
|
if (slot_count <= 0 || current_time <= 0) {
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
timer->slot_count = slot_count;
|
||||||
|
timer->base_time = current_time; //base time for slot 0
|
||||||
|
timer->current_time = current_time;
|
||||||
|
bytes = sizeof(FastTimerSlot) * slot_count;
|
||||||
|
timer->slots = (FastTimerSlot *)malloc(bytes);
|
||||||
|
if (timer->slots == NULL) {
|
||||||
|
return errno != 0 ? errno : ENOMEM;
|
||||||
|
}
|
||||||
|
memset(timer->slots, 0, bytes);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fast_timer_destroy(FastTimer *timer)
|
||||||
|
{
|
||||||
|
if (timer->slots != NULL) {
|
||||||
|
free(timer->slots);
|
||||||
|
timer->slots = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define TIMER_GET_SLOT_INDEX(timer, expires) \
|
||||||
|
(((expires) - timer->base_time) % timer->slot_count)
|
||||||
|
|
||||||
|
#define TIMER_GET_SLOT_POINTER(timer, expires) \
|
||||||
|
(timer->slots + TIMER_GET_SLOT_INDEX(timer, expires))
|
||||||
|
|
||||||
|
int fast_timer_add(FastTimer *timer, FastTimerEntry *entry)
|
||||||
|
{
|
||||||
|
FastTimerSlot *slot;
|
||||||
|
|
||||||
|
slot = TIMER_GET_SLOT_POINTER(timer, entry->expires >
|
||||||
|
timer->current_time ? entry->expires : timer->current_time);
|
||||||
|
entry->next = slot->head.next;
|
||||||
|
if (slot->head.next != NULL) {
|
||||||
|
slot->head.next->prev = entry;
|
||||||
|
}
|
||||||
|
entry->prev = &slot->head;
|
||||||
|
slot->head.next = entry;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fast_timer_modify(FastTimer *timer, FastTimerEntry *entry,
|
||||||
|
const int64_t new_expires)
|
||||||
|
{
|
||||||
|
if (new_expires == entry->expires) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new_expires < entry->expires) {
|
||||||
|
fast_timer_remove(timer, entry);
|
||||||
|
entry->expires = new_expires;
|
||||||
|
return fast_timer_add(timer, entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
entry->rehash = TIMER_GET_SLOT_INDEX(timer, new_expires) !=
|
||||||
|
TIMER_GET_SLOT_INDEX(timer, entry->expires);
|
||||||
|
entry->expires = new_expires; //lazy move
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fast_timer_remove(FastTimer *timer, FastTimerEntry *entry)
|
||||||
|
{
|
||||||
|
if (entry->prev == NULL) {
|
||||||
|
return ENOENT; //already removed
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry->next != NULL) {
|
||||||
|
entry->next->prev = entry->prev;
|
||||||
|
entry->prev->next = entry->next;
|
||||||
|
entry->next = NULL;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
entry->prev->next = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry->prev = NULL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
FastTimerSlot *fast_timer_slot_get(FastTimer *timer, const int64_t current_time)
|
||||||
|
{
|
||||||
|
if (timer->current_time >= current_time) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TIMER_GET_SLOT_POINTER(timer, timer->current_time++);
|
||||||
|
}
|
||||||
|
|
||||||
|
int fast_timer_timeouts_get(FastTimer *timer, const int64_t current_time,
|
||||||
|
FastTimerEntry *head)
|
||||||
|
{
|
||||||
|
FastTimerSlot *slot;
|
||||||
|
FastTimerEntry *entry;
|
||||||
|
FastTimerEntry *first;
|
||||||
|
FastTimerEntry *last;
|
||||||
|
FastTimerEntry *tail;
|
||||||
|
int count;
|
||||||
|
|
||||||
|
head->prev = NULL;
|
||||||
|
head->next = NULL;
|
||||||
|
if (timer->current_time >= current_time) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
first = NULL;
|
||||||
|
last = NULL;
|
||||||
|
tail = head;
|
||||||
|
count = 0;
|
||||||
|
while (timer->current_time < current_time) {
|
||||||
|
slot = TIMER_GET_SLOT_POINTER(timer, timer->current_time++);
|
||||||
|
entry = slot->head.next;
|
||||||
|
while (entry != NULL) {
|
||||||
|
if (entry->expires >= current_time) { //not expired
|
||||||
|
if (first != NULL) {
|
||||||
|
first->prev->next = entry;
|
||||||
|
entry->prev = first->prev;
|
||||||
|
|
||||||
|
tail->next = first;
|
||||||
|
first->prev = tail;
|
||||||
|
tail = last;
|
||||||
|
first = NULL;
|
||||||
|
}
|
||||||
|
if (entry->rehash) {
|
||||||
|
last = entry;
|
||||||
|
entry = entry->next;
|
||||||
|
|
||||||
|
last->rehash = false;
|
||||||
|
fast_timer_remove(timer, last);
|
||||||
|
fast_timer_add(timer, last);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
count++;
|
||||||
|
if (first == NULL) {
|
||||||
|
first = entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
last = entry;
|
||||||
|
entry = entry->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (first != NULL) {
|
||||||
|
first->prev->next = NULL;
|
||||||
|
|
||||||
|
tail->next = first;
|
||||||
|
first->prev = tail;
|
||||||
|
tail = last;
|
||||||
|
first = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count > 0) {
|
||||||
|
tail->next = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
#ifndef __FAST_TIMER_H__
|
||||||
|
#define __FAST_TIMER_H__
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "common_define.h"
|
||||||
|
|
||||||
|
typedef struct fast_timer_entry {
|
||||||
|
int64_t expires;
|
||||||
|
void *data;
|
||||||
|
struct fast_timer_entry *prev;
|
||||||
|
struct fast_timer_entry *next;
|
||||||
|
bool rehash;
|
||||||
|
} FastTimerEntry;
|
||||||
|
|
||||||
|
typedef struct fast_timer_slot {
|
||||||
|
struct fast_timer_entry head;
|
||||||
|
} FastTimerSlot;
|
||||||
|
|
||||||
|
typedef struct fast_timer {
|
||||||
|
int slot_count; //time wheel slot count
|
||||||
|
int64_t base_time; //base time for slot 0
|
||||||
|
int64_t current_time;
|
||||||
|
FastTimerSlot *slots;
|
||||||
|
} FastTimer;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int fast_timer_init(FastTimer *timer, const int slot_count,
|
||||||
|
const int64_t current_time);
|
||||||
|
void fast_timer_destroy(FastTimer *timer);
|
||||||
|
|
||||||
|
int fast_timer_add(FastTimer *timer, FastTimerEntry *entry);
|
||||||
|
int fast_timer_remove(FastTimer *timer, FastTimerEntry *entry);
|
||||||
|
int fast_timer_modify(FastTimer *timer, FastTimerEntry *entry,
|
||||||
|
const int64_t new_expires);
|
||||||
|
|
||||||
|
FastTimerSlot *fast_timer_slot_get(FastTimer *timer, const int64_t current_time);
|
||||||
|
int fast_timer_timeouts_get(FastTimer *timer, const int64_t current_time,
|
||||||
|
FastTimerEntry *head);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2008 Happy Fish / YuQing
|
||||||
|
*
|
||||||
|
* FastDFS may be copied only under the terms of the GNU General
|
||||||
|
* Public License V3, which may be found in the FastDFS source kit.
|
||||||
|
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||||
|
**/
|
||||||
|
|
||||||
|
//fdfs_define.h
|
||||||
|
|
||||||
|
#ifndef _FDFS_DEFINE_H_
|
||||||
|
#define _FDFS_DEFINE_H_
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
#include "common_define.h"
|
||||||
|
|
||||||
|
#define FDFS_TRACKER_SERVER_DEF_PORT 22000
|
||||||
|
#define FDFS_STORAGE_SERVER_DEF_PORT 23000
|
||||||
|
#define FDFS_DEF_STORAGE_RESERVED_MB 1024
|
||||||
|
#define TRACKER_ERROR_LOG_FILENAME "trackerd"
|
||||||
|
#define STORAGE_ERROR_LOG_FILENAME "storaged"
|
||||||
|
|
||||||
|
#define FDFS_RECORD_SEPERATOR '\x01'
|
||||||
|
#define FDFS_FIELD_SEPERATOR '\x02'
|
||||||
|
|
||||||
|
#define SYNC_BINLOG_BUFF_DEF_INTERVAL 60
|
||||||
|
#define CHECK_ACTIVE_DEF_INTERVAL 100
|
||||||
|
|
||||||
|
#define DEFAULT_STORAGE_SYNC_FILE_MAX_DELAY 86400
|
||||||
|
#define DEFAULT_STORAGE_SYNC_FILE_MAX_TIME 300
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
@ -0,0 +1,149 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2008 Happy Fish / YuQing
|
||||||
|
*
|
||||||
|
* FastDFS may be copied only under the terms of the GNU General
|
||||||
|
* Public License V3, which may be found in the FastDFS source kit.
|
||||||
|
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||||
|
**/
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include "logger.h"
|
||||||
|
#include "fdfs_global.h"
|
||||||
|
|
||||||
|
int g_fdfs_connect_timeout = DEFAULT_CONNECT_TIMEOUT;
|
||||||
|
int g_fdfs_network_timeout = DEFAULT_NETWORK_TIMEOUT;
|
||||||
|
char g_fdfs_base_path[MAX_PATH_SIZE] = {'/', 't', 'm', 'p', '\0'};
|
||||||
|
Version g_fdfs_version = {5, 2};
|
||||||
|
bool g_use_connection_pool = false;
|
||||||
|
ConnectionPool g_connection_pool;
|
||||||
|
int g_connection_pool_max_idle_time = 3600;
|
||||||
|
|
||||||
|
/*
|
||||||
|
data filename format:
|
||||||
|
HH/HH/filename: HH for 2 uppercase hex chars
|
||||||
|
*/
|
||||||
|
int fdfs_check_data_filename(const char *filename, const int len)
|
||||||
|
{
|
||||||
|
if (len < 6)
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"the length=%d of filename \"%s\" is too short", \
|
||||||
|
__LINE__, len, filename);
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IS_UPPER_HEX(*filename) || !IS_UPPER_HEX(*(filename+1)) || \
|
||||||
|
*(filename+2) != '/' || \
|
||||||
|
!IS_UPPER_HEX(*(filename+3)) || !IS_UPPER_HEX(*(filename+4)) || \
|
||||||
|
*(filename+5) != '/')
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"the format of filename \"%s\" is invalid", \
|
||||||
|
__LINE__, filename);
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strchr(filename + 6, '/') != NULL)
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"the format of filename \"%s\" is invalid", \
|
||||||
|
__LINE__, filename);
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fdfs_gen_slave_filename(const char *master_filename, \
|
||||||
|
const char *prefix_name, const char *ext_name, \
|
||||||
|
char *filename, int *filename_len)
|
||||||
|
{
|
||||||
|
char true_ext_name[FDFS_FILE_EXT_NAME_MAX_LEN + 2];
|
||||||
|
char *pDot;
|
||||||
|
int master_file_len;
|
||||||
|
|
||||||
|
master_file_len = strlen(master_filename);
|
||||||
|
if (master_file_len < 28 + FDFS_FILE_EXT_NAME_MAX_LEN)
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"master filename \"%s\" is invalid", \
|
||||||
|
__LINE__, master_filename);
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
pDot = strchr(master_filename + (master_file_len - \
|
||||||
|
(FDFS_FILE_EXT_NAME_MAX_LEN + 1)), '.');
|
||||||
|
if (ext_name != NULL)
|
||||||
|
{
|
||||||
|
if (*ext_name == '\0')
|
||||||
|
{
|
||||||
|
*true_ext_name = '\0';
|
||||||
|
}
|
||||||
|
else if (*ext_name == '.')
|
||||||
|
{
|
||||||
|
snprintf(true_ext_name, sizeof(true_ext_name), \
|
||||||
|
"%s", ext_name);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
snprintf(true_ext_name, sizeof(true_ext_name), \
|
||||||
|
".%s", ext_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (pDot == NULL)
|
||||||
|
{
|
||||||
|
*true_ext_name = '\0';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
strcpy(true_ext_name, pDot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*true_ext_name == '\0' && strcmp(prefix_name, "-m") == 0)
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"prefix_name \"%s\" is invalid", \
|
||||||
|
__LINE__, prefix_name);
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* when prefix_name is empty, the extension name of master file and
|
||||||
|
slave file can not be same
|
||||||
|
*/
|
||||||
|
if ((*prefix_name == '\0') && ((pDot == NULL && *true_ext_name == '\0')
|
||||||
|
|| (pDot != NULL && strcmp(pDot, true_ext_name) == 0)))
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"empty prefix_name is not allowed", __LINE__);
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pDot == NULL)
|
||||||
|
{
|
||||||
|
*filename_len = sprintf(filename, "%s%s%s", master_filename, \
|
||||||
|
prefix_name, true_ext_name);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*filename_len = pDot - master_filename;
|
||||||
|
memcpy(filename, master_filename, *filename_len);
|
||||||
|
*filename_len += sprintf(filename + *filename_len, "%s%s", \
|
||||||
|
prefix_name, true_ext_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2008 Happy Fish / YuQing
|
||||||
|
*
|
||||||
|
* FastDFS may be copied only under the terms of the GNU General
|
||||||
|
* Public License V3, which may be found in the FastDFS source kit.
|
||||||
|
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||||
|
**/
|
||||||
|
|
||||||
|
//fdfs_global.h
|
||||||
|
|
||||||
|
#ifndef _FDFS_GLOBAL_H
|
||||||
|
#define _FDFS_GLOBAL_H
|
||||||
|
|
||||||
|
#include "common_define.h"
|
||||||
|
#include "fdfs_define.h"
|
||||||
|
#include "connection_pool.h"
|
||||||
|
|
||||||
|
#define FDFS_FILE_EXT_NAME_MAX_LEN 6
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern int g_fdfs_connect_timeout;
|
||||||
|
extern int g_fdfs_network_timeout;
|
||||||
|
extern char g_fdfs_base_path[MAX_PATH_SIZE];
|
||||||
|
extern Version g_fdfs_version;
|
||||||
|
extern bool g_use_connection_pool;
|
||||||
|
extern ConnectionPool g_connection_pool;
|
||||||
|
extern int g_connection_pool_max_idle_time;
|
||||||
|
|
||||||
|
int fdfs_check_data_filename(const char *filename, const int len);
|
||||||
|
int fdfs_gen_slave_filename(const char *master_filename, \
|
||||||
|
const char *prefix_name, const char *ext_name, \
|
||||||
|
char *filename, int *filename_len);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
@ -0,0 +1,372 @@
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2008 Happy Fish / YuQing
|
||||||
|
*
|
||||||
|
* FastDFS may be copied only under the terms of the GNU General
|
||||||
|
* Public License V3, which may be found in the FastDFS source kit.
|
||||||
|
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||||
|
**/
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include "logger.h"
|
||||||
|
#include "md5.h"
|
||||||
|
#include "shared_func.h"
|
||||||
|
#include "mime_file_parser.h"
|
||||||
|
#include "fdfs_global.h"
|
||||||
|
#include "fdfs_http_shared.h"
|
||||||
|
|
||||||
|
const char *fdfs_http_get_file_extension(const char *filename, \
|
||||||
|
const int filename_len, int *ext_len)
|
||||||
|
{
|
||||||
|
const char *pEnd;
|
||||||
|
const char *pExtName;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
pEnd = filename + filename_len;
|
||||||
|
pExtName = pEnd - 1;
|
||||||
|
for (i=0; i<FDFS_FILE_EXT_NAME_MAX_LEN && pExtName >= filename; \
|
||||||
|
i++, pExtName--)
|
||||||
|
{
|
||||||
|
if (*pExtName == '.')
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i < FDFS_FILE_EXT_NAME_MAX_LEN) //found
|
||||||
|
{
|
||||||
|
pExtName++; //skip .
|
||||||
|
*ext_len = pEnd - pExtName;
|
||||||
|
return pExtName;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*ext_len = 0;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int fdfs_http_get_content_type_by_extname(FDFSHTTPParams *pParams, \
|
||||||
|
const char *ext_name, const int ext_len, \
|
||||||
|
char *content_type, const int content_type_size)
|
||||||
|
{
|
||||||
|
HashData *pHashData;
|
||||||
|
|
||||||
|
if (ext_len == 0)
|
||||||
|
{
|
||||||
|
logWarning("file: "__FILE__", line: %d, " \
|
||||||
|
"extension name is empty, " \
|
||||||
|
"set to default content type: %s", \
|
||||||
|
__LINE__, pParams->default_content_type);
|
||||||
|
strcpy(content_type, pParams->default_content_type);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pHashData = hash_find_ex(&pParams->content_type_hash, \
|
||||||
|
ext_name, ext_len + 1);
|
||||||
|
if (pHashData == NULL)
|
||||||
|
{
|
||||||
|
logWarning("file: "__FILE__", line: %d, " \
|
||||||
|
"extension name: %s is not supported, " \
|
||||||
|
"set to default content type: %s", \
|
||||||
|
__LINE__, ext_name, pParams->default_content_type);
|
||||||
|
strcpy(content_type, pParams->default_content_type);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pHashData->value_len >= content_type_size)
|
||||||
|
{
|
||||||
|
*content_type = '\0';
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"extension name: %s 's content type " \
|
||||||
|
"is too long", __LINE__, ext_name);
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(content_type, pHashData->value, pHashData->value_len);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fdfs_http_params_load(IniContext *pIniContext, \
|
||||||
|
const char *conf_filename, FDFSHTTPParams *pParams)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
int ext_len;
|
||||||
|
const char *ext_name;
|
||||||
|
char *mime_types_filename;
|
||||||
|
char szMimeFilename[256];
|
||||||
|
char *anti_steal_secret_key;
|
||||||
|
char *token_check_fail_filename;
|
||||||
|
char *default_content_type;
|
||||||
|
int def_content_type_len;
|
||||||
|
int64_t file_size;
|
||||||
|
|
||||||
|
memset(pParams, 0, sizeof(FDFSHTTPParams));
|
||||||
|
|
||||||
|
pParams->disabled = iniGetBoolValue(NULL, "http.disabled", \
|
||||||
|
pIniContext, false);
|
||||||
|
if (pParams->disabled)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pParams->need_find_content_type = iniGetBoolValue(NULL, \
|
||||||
|
"http.need_find_content_type", \
|
||||||
|
pIniContext, true);
|
||||||
|
|
||||||
|
pParams->server_port = iniGetIntValue(NULL, "http.server_port", \
|
||||||
|
pIniContext, 80);
|
||||||
|
if (pParams->server_port <= 0)
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"invalid param \"http.server_port\": %d", \
|
||||||
|
__LINE__, pParams->server_port);
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
pParams->anti_steal_token = iniGetBoolValue(NULL, \
|
||||||
|
"http.anti_steal.check_token", \
|
||||||
|
pIniContext, false);
|
||||||
|
if (pParams->need_find_content_type || pParams->anti_steal_token)
|
||||||
|
{
|
||||||
|
mime_types_filename = iniGetStrValue(NULL, "http.mime_types_filename", \
|
||||||
|
pIniContext);
|
||||||
|
if (mime_types_filename == NULL || *mime_types_filename == '\0')
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"param \"http.mime_types_filename\" not exist " \
|
||||||
|
"or is empty", __LINE__);
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strncasecmp(mime_types_filename, "http://", 7) != 0 && \
|
||||||
|
*mime_types_filename != '/' && \
|
||||||
|
strncasecmp(conf_filename, "http://", 7) != 0)
|
||||||
|
{
|
||||||
|
char *pPathEnd;
|
||||||
|
|
||||||
|
pPathEnd = strrchr(conf_filename, '/');
|
||||||
|
if (pPathEnd == NULL)
|
||||||
|
{
|
||||||
|
snprintf(szMimeFilename, sizeof(szMimeFilename), \
|
||||||
|
"%s", mime_types_filename);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int nPathLen;
|
||||||
|
int nFilenameLen;
|
||||||
|
|
||||||
|
nPathLen = (pPathEnd - conf_filename) + 1;
|
||||||
|
nFilenameLen = strlen(mime_types_filename);
|
||||||
|
if (nPathLen + nFilenameLen >= sizeof(szMimeFilename))
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"filename is too long, length %d >= %d",
|
||||||
|
__LINE__, nPathLen + nFilenameLen, \
|
||||||
|
(int)sizeof(szMimeFilename));
|
||||||
|
return ENOSPC;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(szMimeFilename, conf_filename, nPathLen);
|
||||||
|
memcpy(szMimeFilename + nPathLen, mime_types_filename, \
|
||||||
|
nFilenameLen);
|
||||||
|
*(szMimeFilename + nPathLen + nFilenameLen) = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
snprintf(szMimeFilename, sizeof(szMimeFilename), \
|
||||||
|
"%s", mime_types_filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
result = load_mime_types_from_file(&pParams->content_type_hash, \
|
||||||
|
szMimeFilename);
|
||||||
|
if (result != 0)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
default_content_type = iniGetStrValue(NULL, \
|
||||||
|
"http.default_content_type", \
|
||||||
|
pIniContext);
|
||||||
|
if (default_content_type == NULL || *default_content_type == '\0')
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"param \"http.default_content_type\" not exist " \
|
||||||
|
"or is empty", __LINE__);
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
def_content_type_len = strlen(default_content_type);
|
||||||
|
if (def_content_type_len >= sizeof(pParams->default_content_type))
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"default content type: %s is too long", \
|
||||||
|
__LINE__, default_content_type);
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
memcpy(pParams->default_content_type, default_content_type, \
|
||||||
|
def_content_type_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pParams->anti_steal_token)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pParams->token_ttl = iniGetIntValue(NULL, \
|
||||||
|
"http.anti_steal.token_ttl", \
|
||||||
|
pIniContext, 600);
|
||||||
|
if (pParams->token_ttl <= 0)
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"param \"http.anti_steal.token_ttl\" is invalid", \
|
||||||
|
__LINE__);
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
anti_steal_secret_key = iniGetStrValue(NULL, \
|
||||||
|
"http.anti_steal.secret_key", \
|
||||||
|
pIniContext);
|
||||||
|
if (anti_steal_secret_key == NULL || *anti_steal_secret_key == '\0')
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"param \"http.anti_steal.secret_key\" not exist " \
|
||||||
|
"or is empty", __LINE__);
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer_strcpy(&pParams->anti_steal_secret_key, anti_steal_secret_key);
|
||||||
|
|
||||||
|
token_check_fail_filename = iniGetStrValue(NULL, \
|
||||||
|
"http.anti_steal.token_check_fail", \
|
||||||
|
pIniContext);
|
||||||
|
if (token_check_fail_filename == NULL || \
|
||||||
|
*token_check_fail_filename == '\0')
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fileExists(token_check_fail_filename))
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"token_check_fail file: %s not exists", __LINE__, \
|
||||||
|
token_check_fail_filename);
|
||||||
|
return ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
ext_name = fdfs_http_get_file_extension(token_check_fail_filename, \
|
||||||
|
strlen(token_check_fail_filename), &ext_len);
|
||||||
|
if ((result=fdfs_http_get_content_type_by_extname(pParams, \
|
||||||
|
ext_name, ext_len, \
|
||||||
|
pParams->token_check_fail_content_type, \
|
||||||
|
sizeof(pParams->token_check_fail_content_type))) != 0)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pParams->need_find_content_type)
|
||||||
|
{
|
||||||
|
hash_destroy(&pParams->content_type_hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((result=getFileContent(token_check_fail_filename, \
|
||||||
|
&pParams->token_check_fail_buff.buff, &file_size)) != 0)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
pParams->token_check_fail_buff.alloc_size = file_size;
|
||||||
|
pParams->token_check_fail_buff.length = file_size;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fdfs_http_params_destroy(FDFSHTTPParams *pParams)
|
||||||
|
{
|
||||||
|
if (pParams->need_find_content_type)
|
||||||
|
{
|
||||||
|
hash_destroy(&pParams->content_type_hash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int fdfs_http_gen_token(const BufferInfo *secret_key, const char *file_id, \
|
||||||
|
const int timestamp, char *token)
|
||||||
|
{
|
||||||
|
char buff[256 + 64];
|
||||||
|
unsigned char digit[16];
|
||||||
|
int id_len;
|
||||||
|
int total_len;
|
||||||
|
|
||||||
|
id_len = strlen(file_id);
|
||||||
|
if (id_len + secret_key->length + 12 > sizeof(buff))
|
||||||
|
{
|
||||||
|
return ENOSPC;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(buff, file_id, id_len);
|
||||||
|
total_len = id_len;
|
||||||
|
memcpy(buff + total_len, secret_key->buff, secret_key->length);
|
||||||
|
total_len += secret_key->length;
|
||||||
|
total_len += sprintf(buff + total_len, "%d", timestamp);
|
||||||
|
|
||||||
|
my_md5_buffer(buff, total_len, digit);
|
||||||
|
bin2hex((char *)digit, 16, token);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fdfs_http_check_token(const BufferInfo *secret_key, const char *file_id, \
|
||||||
|
const int timestamp, const char *token, const int ttl)
|
||||||
|
{
|
||||||
|
char true_token[33];
|
||||||
|
int result;
|
||||||
|
int token_len;
|
||||||
|
|
||||||
|
token_len = strlen(token);
|
||||||
|
if (token_len != 32)
|
||||||
|
{
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((timestamp != 0) && (time(NULL) - timestamp > ttl))
|
||||||
|
{
|
||||||
|
return ETIMEDOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((result=fdfs_http_gen_token(secret_key, file_id, \
|
||||||
|
timestamp, true_token)) != 0)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (memcmp(token, true_token, 32) == 0) ? 0 : EPERM;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *fdfs_http_get_parameter(const char *param_name, KeyValuePair *params, \
|
||||||
|
const int param_count)
|
||||||
|
{
|
||||||
|
KeyValuePair *pCurrent;
|
||||||
|
KeyValuePair *pEnd;
|
||||||
|
|
||||||
|
pEnd = params + param_count;
|
||||||
|
for (pCurrent=params; pCurrent<pEnd; pCurrent++)
|
||||||
|
{
|
||||||
|
if (strcmp(pCurrent->key, param_name) == 0)
|
||||||
|
{
|
||||||
|
return pCurrent->value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,125 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2008 Happy Fish / YuQing
|
||||||
|
*
|
||||||
|
* FastDFS may be copied only under the terms of the GNU General
|
||||||
|
* Public License V3, which may be found in the FastDFS source kit.
|
||||||
|
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||||
|
**/
|
||||||
|
|
||||||
|
#ifndef _FDFS_HTTP_SHARED_H
|
||||||
|
#define _FDFS_HTTP_SHARED_H
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include "ini_file_reader.h"
|
||||||
|
#include "hash.h"
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
bool disabled;
|
||||||
|
bool anti_steal_token;
|
||||||
|
|
||||||
|
/* if need find content type by file extension name */
|
||||||
|
bool need_find_content_type;
|
||||||
|
|
||||||
|
/* the web server port */
|
||||||
|
int server_port;
|
||||||
|
|
||||||
|
/* key is file ext name, value is content type */
|
||||||
|
HashArray content_type_hash;
|
||||||
|
|
||||||
|
BufferInfo anti_steal_secret_key;
|
||||||
|
BufferInfo token_check_fail_buff;
|
||||||
|
char default_content_type[64];
|
||||||
|
char token_check_fail_content_type[64];
|
||||||
|
int token_ttl;
|
||||||
|
} FDFSHTTPParams;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
load HTTP params from conf file
|
||||||
|
params:
|
||||||
|
pIniContext: the ini file items, return by iniLoadItems
|
||||||
|
conf_filename: config filename
|
||||||
|
pHTTPParams: the HTTP params
|
||||||
|
return: 0 for success, != 0 fail
|
||||||
|
**/
|
||||||
|
int fdfs_http_params_load(IniContext *pIniContext, \
|
||||||
|
const char *conf_filename, FDFSHTTPParams *pHTTPParams);
|
||||||
|
|
||||||
|
void fdfs_http_params_destroy(FDFSHTTPParams *pParams);
|
||||||
|
|
||||||
|
/**
|
||||||
|
generate anti-steal token
|
||||||
|
params:
|
||||||
|
secret_key: secret key buffer
|
||||||
|
file_id: FastDFS file id
|
||||||
|
timestamp: current timestamp, unix timestamp (seconds), 0 for never timeout
|
||||||
|
token: return token buffer
|
||||||
|
return: 0 for success, != 0 fail
|
||||||
|
**/
|
||||||
|
int fdfs_http_gen_token(const BufferInfo *secret_key, const char *file_id, \
|
||||||
|
const int timestamp, char *token);
|
||||||
|
|
||||||
|
/**
|
||||||
|
check anti-steal token
|
||||||
|
params:
|
||||||
|
secret_key: secret key buffer
|
||||||
|
file_id: FastDFS file id
|
||||||
|
timestamp: the timestamp to generate the token, unix timestamp (seconds)
|
||||||
|
token: token buffer
|
||||||
|
ttl: token ttl, delta seconds
|
||||||
|
return: 0 for passed, != 0 fail
|
||||||
|
**/
|
||||||
|
int fdfs_http_check_token(const BufferInfo *secret_key, const char *file_id, \
|
||||||
|
const int timestamp, const char *token, const int ttl);
|
||||||
|
|
||||||
|
/**
|
||||||
|
get parameter value
|
||||||
|
params:
|
||||||
|
param_name: the parameter name to get
|
||||||
|
params: parameter array
|
||||||
|
param_count: param count
|
||||||
|
return: param value pointer, return NULL if not exist
|
||||||
|
**/
|
||||||
|
char *fdfs_http_get_parameter(const char *param_name, KeyValuePair *params, \
|
||||||
|
const int param_count);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
get file extension name
|
||||||
|
params:
|
||||||
|
filename: the filename
|
||||||
|
filename_len: the length of filename
|
||||||
|
ext_len: return the length of extension name
|
||||||
|
return: extension name, NULL for none
|
||||||
|
**/
|
||||||
|
const char *fdfs_http_get_file_extension(const char *filename, \
|
||||||
|
const int filename_len, int *ext_len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
get content type by file extension name
|
||||||
|
params:
|
||||||
|
pHTTPParams: the HTTP params
|
||||||
|
ext_name: the extension name
|
||||||
|
ext_len: the length of extension name
|
||||||
|
content_type: return content type
|
||||||
|
content_type_size: content type buffer size
|
||||||
|
return: 0 for success, != 0 fail
|
||||||
|
**/
|
||||||
|
int fdfs_http_get_content_type_by_extname(FDFSHTTPParams *pParams, \
|
||||||
|
const char *ext_name, const int ext_len, \
|
||||||
|
char *content_type, const int content_type_size);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,381 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2008 Happy Fish / YuQing
|
||||||
|
*
|
||||||
|
* FastDFS may be copied only under the terms of the GNU General
|
||||||
|
* Public License V3, which may be found in the FastDFS source kit.
|
||||||
|
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||||
|
**/
|
||||||
|
|
||||||
|
#ifndef _HASH_H_
|
||||||
|
#define _HASH_H_
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include "common_define.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define CRC32_XINIT 0xFFFFFFFF /* initial value */
|
||||||
|
#define CRC32_XOROT 0xFFFFFFFF /* final xor value */
|
||||||
|
|
||||||
|
typedef int (*HashFunc) (const void *key, const int key_len);
|
||||||
|
|
||||||
|
#ifdef HASH_STORE_HASH_CODE
|
||||||
|
#define HASH_CODE(pHash, hash_data) hash_data->hash_code
|
||||||
|
#else
|
||||||
|
#define HASH_CODE(pHash, hash_data) ((unsigned int)pHash->hash_func( \
|
||||||
|
hash_data->key, hash_data->key_len))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define CALC_NODE_MALLOC_BYTES(key_len, value_size) \
|
||||||
|
sizeof(HashData) + key_len + value_size
|
||||||
|
|
||||||
|
#define FREE_HASH_DATA(pHash, hash_data) \
|
||||||
|
pHash->item_count--; \
|
||||||
|
pHash->bytes_used -= CALC_NODE_MALLOC_BYTES(hash_data->key_len, \
|
||||||
|
hash_data->malloc_value_size); \
|
||||||
|
free(hash_data);
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct tagHashData
|
||||||
|
{
|
||||||
|
int key_len;
|
||||||
|
int value_len;
|
||||||
|
int malloc_value_size;
|
||||||
|
|
||||||
|
#ifdef HASH_STORE_HASH_CODE
|
||||||
|
unsigned int hash_code;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
char *value;
|
||||||
|
struct tagHashData *next;
|
||||||
|
char key[0];
|
||||||
|
} HashData;
|
||||||
|
|
||||||
|
typedef int64_t (*ConvertValueFunc)(const HashData *old_data, const int inc,
|
||||||
|
char *new_value, int *new_value_len, void *arg);
|
||||||
|
|
||||||
|
typedef struct tagHashArray
|
||||||
|
{
|
||||||
|
HashData **buckets;
|
||||||
|
HashFunc hash_func;
|
||||||
|
int item_count;
|
||||||
|
unsigned int *capacity;
|
||||||
|
double load_factor;
|
||||||
|
int64_t max_bytes;
|
||||||
|
int64_t bytes_used;
|
||||||
|
bool is_malloc_capacity;
|
||||||
|
bool is_malloc_value;
|
||||||
|
unsigned int lock_count;
|
||||||
|
pthread_mutex_t *locks;
|
||||||
|
} HashArray;
|
||||||
|
|
||||||
|
typedef struct tagHashStat
|
||||||
|
{
|
||||||
|
unsigned int capacity;
|
||||||
|
int item_count;
|
||||||
|
int bucket_used;
|
||||||
|
double bucket_avg_length;
|
||||||
|
int bucket_max_length;
|
||||||
|
} HashStat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hash walk function
|
||||||
|
* parameters:
|
||||||
|
* index: item index based 0
|
||||||
|
* data: hash data, including key and value
|
||||||
|
* args: passed by hash_walk function
|
||||||
|
* return 0 for success, != 0 for error
|
||||||
|
*/
|
||||||
|
typedef int (*HashWalkFunc)(const int index, const HashData *data, void *args);
|
||||||
|
|
||||||
|
#define hash_init(pHash, hash_func, capacity, load_factor) \
|
||||||
|
hash_init_ex(pHash, hash_func, capacity, load_factor, 0, false)
|
||||||
|
|
||||||
|
#define hash_insert(pHash, key, key_len, value) \
|
||||||
|
hash_insert_ex(pHash, key, key_len, value, 0, true)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hash init function
|
||||||
|
* parameters:
|
||||||
|
* pHash: the hash table
|
||||||
|
* hash_func: hash function
|
||||||
|
* capacity: init capacity
|
||||||
|
* load_factor: hash load factor, such as 0.75
|
||||||
|
* max_bytes: max memory can be used (bytes)
|
||||||
|
* bMallocValue: if need malloc value buffer
|
||||||
|
* return 0 for success, != 0 for error
|
||||||
|
*/
|
||||||
|
int hash_init_ex(HashArray *pHash, HashFunc hash_func, \
|
||||||
|
const unsigned int capacity, const double load_factor, \
|
||||||
|
const int64_t max_bytes, const bool bMallocValue);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set hash locks function
|
||||||
|
* parameters:
|
||||||
|
* lock_count: the lock count
|
||||||
|
* return 0 for success, != 0 for error
|
||||||
|
*/
|
||||||
|
int hash_set_locks(HashArray *pHash, const int lock_count);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* convert the value
|
||||||
|
* parameters:
|
||||||
|
* HashData: the old hash data
|
||||||
|
* inc: the increasement value
|
||||||
|
* new_value: return the new value
|
||||||
|
* new_value_len: return the length of the new value
|
||||||
|
* arg: the user data
|
||||||
|
* return the number after increasement
|
||||||
|
*/
|
||||||
|
int64_t hash_inc_value(const HashData *old_data, const int inc,
|
||||||
|
char *new_value, int *new_value_len, void *arg);
|
||||||
|
|
||||||
|
#define hash_inc(pHash, key, key_len, inc, value, value_len) \
|
||||||
|
hash_inc_ex(pHash, key, key_len, inc, value, value_len, \
|
||||||
|
hash_inc_value, NULL)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* atomic increase value
|
||||||
|
* parameters:
|
||||||
|
* pHash: the hash table
|
||||||
|
* key: the key to insert
|
||||||
|
* key_len: length of th key
|
||||||
|
* inc: the increasement value
|
||||||
|
* value: return the new value
|
||||||
|
* value_len: return the length of the new value
|
||||||
|
* convert_func: the convert function
|
||||||
|
* arg: the arg to convert function
|
||||||
|
* return 0 for success, != 0 for error (errno)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
int hash_inc_ex(HashArray *pHash, const void *key, const int key_len,
|
||||||
|
const int inc, char *value, int *value_len,
|
||||||
|
ConvertValueFunc convert_func, void *arg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hash destroy function
|
||||||
|
* parameters:
|
||||||
|
* pHash: the hash table
|
||||||
|
* return none
|
||||||
|
*/
|
||||||
|
void hash_destroy(HashArray *pHash);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hash insert key
|
||||||
|
* parameters:
|
||||||
|
* pHash: the hash table
|
||||||
|
* key: the key to insert
|
||||||
|
* key_len: length of th key
|
||||||
|
* value: the value
|
||||||
|
* value_len: length of the value
|
||||||
|
* needLock: if need lock
|
||||||
|
* return >= 0 for success, 0 for key already exist (update),
|
||||||
|
* 1 for new key (insert), < 0 for error
|
||||||
|
*/
|
||||||
|
int hash_insert_ex(HashArray *pHash, const void *key, const int key_len, \
|
||||||
|
void *value, const int value_len, const bool needLock);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hash find key
|
||||||
|
* parameters:
|
||||||
|
* pHash: the hash table
|
||||||
|
* key: the key to find
|
||||||
|
* key_len: length of th key
|
||||||
|
* return user data, return NULL when the key not exist
|
||||||
|
*/
|
||||||
|
void *hash_find(HashArray *pHash, const void *key, const int key_len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hash find key
|
||||||
|
* parameters:
|
||||||
|
* pHash: the hash table
|
||||||
|
* key: the key to find
|
||||||
|
* key_len: length of th key
|
||||||
|
* return hash data, return NULL when the key not exist
|
||||||
|
*/
|
||||||
|
HashData *hash_find_ex(HashArray *pHash, const void *key, const int key_len);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hash get the value of the key
|
||||||
|
* parameters:
|
||||||
|
* pHash: the hash table
|
||||||
|
* key: the key to find
|
||||||
|
* key_len: length of th key
|
||||||
|
* value: store the value
|
||||||
|
* value_len: input for the max size of the value
|
||||||
|
* output for the length fo the value
|
||||||
|
* return 0 for success, != 0 fail (errno)
|
||||||
|
*/
|
||||||
|
int hash_get(HashArray *pHash, const void *key, const int key_len,
|
||||||
|
void *value, int *value_len);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hash partial set
|
||||||
|
* parameters:
|
||||||
|
* pHash: the hash table
|
||||||
|
* key: the key to insert
|
||||||
|
* key_len: length of th key
|
||||||
|
* value: the value
|
||||||
|
* offset: the offset of existed value
|
||||||
|
* value_len: length of the value
|
||||||
|
* return 0 for success, != 0 fail (errno)
|
||||||
|
*/
|
||||||
|
int hash_partial_set(HashArray *pHash, const void *key, const int key_len,
|
||||||
|
const char *value, const int offset, const int value_len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hash delete key
|
||||||
|
* parameters:
|
||||||
|
* pHash: the hash table
|
||||||
|
* key: the key to delete
|
||||||
|
* key_len: length of th key
|
||||||
|
* return 0 for success, != 0 fail (errno)
|
||||||
|
*/
|
||||||
|
int hash_delete(HashArray *pHash, const void *key, const int key_len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hash walk (iterator)
|
||||||
|
* parameters:
|
||||||
|
* pHash: the hash table
|
||||||
|
* walkFunc: walk (interator) function
|
||||||
|
* args: extra args which will be passed to walkFunc
|
||||||
|
* return 0 for success, != 0 fail (errno)
|
||||||
|
*/
|
||||||
|
int hash_walk(HashArray *pHash, HashWalkFunc walkFunc, void *args);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get hash item count
|
||||||
|
* parameters:
|
||||||
|
* pHash: the hash table
|
||||||
|
* return item count
|
||||||
|
*/
|
||||||
|
int hash_count(HashArray *pHash);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hash best optimize
|
||||||
|
* parameters:
|
||||||
|
* pHash: the hash table
|
||||||
|
* suggest_capacity: suggest init capacity for speed
|
||||||
|
* return >0 for success, < 0 fail (errno)
|
||||||
|
*/
|
||||||
|
int hash_best_op(HashArray *pHash, const int suggest_capacity);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hash stat
|
||||||
|
* parameters:
|
||||||
|
* pHash: the hash table
|
||||||
|
* pStat: return stat info
|
||||||
|
* stat_by_lens: return stats array by bucket length
|
||||||
|
* stat_by_lens[0] empty buckets count
|
||||||
|
* stat_by_lens[1] contain 1 key buckets count
|
||||||
|
* stat_by_lens[2] contain 2 key buckets count, etc
|
||||||
|
* stat_size: stats array size (contain max elments)
|
||||||
|
* return 0 for success, != 0 fail (errno)
|
||||||
|
*/
|
||||||
|
int hash_stat(HashArray *pHash, HashStat *pStat, \
|
||||||
|
int *stat_by_lens, const int stat_size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* print hash stat info
|
||||||
|
* parameters:
|
||||||
|
* pHash: the hash table
|
||||||
|
* return none
|
||||||
|
*/
|
||||||
|
void hash_stat_print(HashArray *pHash);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* lock the bucket of hash table
|
||||||
|
* parameters:
|
||||||
|
* pHash: the hash table
|
||||||
|
* bucket_index: the index of bucket
|
||||||
|
* return 0 for success, != 0 fail (errno)
|
||||||
|
*/
|
||||||
|
int hash_bucket_lock(HashArray *pHash, const unsigned int bucket_index);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* unlock the bucket of hash table
|
||||||
|
* parameters:
|
||||||
|
* pHash: the hash table
|
||||||
|
* bucket_index: the index of bucket
|
||||||
|
* return 0 for success, != 0 fail (errno)
|
||||||
|
*/
|
||||||
|
int hash_bucket_unlock(HashArray *pHash, const unsigned int bucket_index);
|
||||||
|
|
||||||
|
int RSHash(const void *key, const int key_len);
|
||||||
|
|
||||||
|
int JSHash(const void *key, const int key_len);
|
||||||
|
int JSHash_ex(const void *key, const int key_len, \
|
||||||
|
const int init_value);
|
||||||
|
|
||||||
|
int PJWHash(const void *key, const int key_len);
|
||||||
|
int PJWHash_ex(const void *key, const int key_len, \
|
||||||
|
const int init_value);
|
||||||
|
|
||||||
|
int ELFHash(const void *key, const int key_len);
|
||||||
|
int ELFHash_ex(const void *key, const int key_len, \
|
||||||
|
const int init_value);
|
||||||
|
|
||||||
|
int BKDRHash(const void *key, const int key_len);
|
||||||
|
int BKDRHash_ex(const void *key, const int key_len, \
|
||||||
|
const int init_value);
|
||||||
|
|
||||||
|
int SDBMHash(const void *key, const int key_len);
|
||||||
|
int SDBMHash_ex(const void *key, const int key_len, \
|
||||||
|
const int init_value);
|
||||||
|
|
||||||
|
int Time33Hash(const void *key, const int key_len);
|
||||||
|
int Time33Hash_ex(const void *key, const int key_len, \
|
||||||
|
const int init_value);
|
||||||
|
|
||||||
|
int DJBHash(const void *key, const int key_len);
|
||||||
|
int DJBHash_ex(const void *key, const int key_len, \
|
||||||
|
const int init_value);
|
||||||
|
|
||||||
|
int APHash(const void *key, const int key_len);
|
||||||
|
int APHash_ex(const void *key, const int key_len, \
|
||||||
|
const int init_value);
|
||||||
|
|
||||||
|
int calc_hashnr (const void* key, const int key_len);
|
||||||
|
|
||||||
|
int calc_hashnr1(const void* key, const int key_len);
|
||||||
|
int calc_hashnr1_ex(const void* key, const int key_len, \
|
||||||
|
const int init_value);
|
||||||
|
|
||||||
|
int simple_hash(const void* key, const int key_len);
|
||||||
|
int simple_hash_ex(const void* key, const int key_len, \
|
||||||
|
const int init_value);
|
||||||
|
|
||||||
|
int CRC32(void *key, const int key_len);
|
||||||
|
int CRC32_ex(void *key, const int key_len, \
|
||||||
|
const int init_value);
|
||||||
|
|
||||||
|
#define CRC32_FINAL(crc) (crc ^ CRC32_XOROT)
|
||||||
|
|
||||||
|
#define INIT_HASH_CODES4(hash_codes) \
|
||||||
|
hash_codes[0] = CRC32_XINIT; \
|
||||||
|
hash_codes[1] = 0; \
|
||||||
|
hash_codes[2] = 0; \
|
||||||
|
hash_codes[3] = 0; \
|
||||||
|
|
||||||
|
#define CALC_HASH_CODES4(buff, buff_len, hash_codes) \
|
||||||
|
hash_codes[0] = CRC32_ex(buff, buff_len, hash_codes[0]); \
|
||||||
|
hash_codes[1] = ELFHash_ex(buff, buff_len, hash_codes[1]); \
|
||||||
|
hash_codes[2] = simple_hash_ex(buff, buff_len, hash_codes[2]); \
|
||||||
|
hash_codes[3] = Time33Hash_ex(buff, buff_len, hash_codes[3]); \
|
||||||
|
|
||||||
|
|
||||||
|
#define FINISH_HASH_CODES4(hash_codes) \
|
||||||
|
hash_codes[0] = CRC32_FINAL(hash_codes[0]); \
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
@ -0,0 +1,302 @@
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2008 Happy Fish / YuQing
|
||||||
|
*
|
||||||
|
* FastDFS may be copied only under the terms of the GNU General
|
||||||
|
* Public License V3, which may be found in the FastDFS source kit.
|
||||||
|
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||||
|
**/
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include "sockopt.h"
|
||||||
|
#include "logger.h"
|
||||||
|
#include "shared_func.h"
|
||||||
|
|
||||||
|
int get_url_content(const char *url, const int connect_timeout, \
|
||||||
|
const int network_timeout, int *http_status, \
|
||||||
|
char **content, int *content_len, char *error_info)
|
||||||
|
{
|
||||||
|
char domain_name[256];
|
||||||
|
char ip_addr[IP_ADDRESS_SIZE];
|
||||||
|
char out_buff[4096];
|
||||||
|
int domain_len;
|
||||||
|
int url_len;
|
||||||
|
int out_len;
|
||||||
|
int alloc_size;
|
||||||
|
int recv_bytes;
|
||||||
|
int result;
|
||||||
|
int sock;
|
||||||
|
int port;
|
||||||
|
const char *pDomain;
|
||||||
|
const char *pContent;
|
||||||
|
const char *pURI;
|
||||||
|
char *pPort;
|
||||||
|
char *pSpace;
|
||||||
|
|
||||||
|
*http_status = 0;
|
||||||
|
*content_len = 0;
|
||||||
|
*content = NULL;
|
||||||
|
|
||||||
|
url_len = strlen(url);
|
||||||
|
if (url_len <= 7 || strncasecmp(url, "http://", 7) != 0)
|
||||||
|
{
|
||||||
|
sprintf(error_info, "file: "__FILE__", line: %d, " \
|
||||||
|
"invalid url.", __LINE__);
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
pDomain = url + 7;
|
||||||
|
pURI = strchr(pDomain, '/');
|
||||||
|
if (pURI == NULL)
|
||||||
|
{
|
||||||
|
domain_len = url_len - 7;
|
||||||
|
pURI = "/";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
domain_len = pURI - pDomain;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (domain_len >= sizeof(domain_name))
|
||||||
|
{
|
||||||
|
sprintf(error_info, "file: "__FILE__", line: %d, " \
|
||||||
|
"domain is too large, exceed %d.", \
|
||||||
|
__LINE__, (int)sizeof(domain_name));
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(domain_name, pDomain, domain_len);
|
||||||
|
*(domain_name + domain_len) = '\0';
|
||||||
|
pPort = strchr(domain_name, ':');
|
||||||
|
if (pPort == NULL)
|
||||||
|
{
|
||||||
|
port = 80;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*pPort = '\0';
|
||||||
|
port = atoi(pPort + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getIpaddrByName(domain_name, ip_addr, \
|
||||||
|
sizeof(ip_addr)) == INADDR_NONE)
|
||||||
|
{
|
||||||
|
sprintf(error_info, "file: "__FILE__", line: %d, " \
|
||||||
|
"resolve domain \"%s\" fail.", \
|
||||||
|
__LINE__, domain_name);
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
if(sock < 0)
|
||||||
|
{
|
||||||
|
sprintf(error_info, "file: "__FILE__", line: %d, " \
|
||||||
|
"socket create failed, errno: %d, " \
|
||||||
|
"error info: %s", __LINE__, \
|
||||||
|
errno, STRERROR(errno));
|
||||||
|
return errno != 0 ? errno : EPERM;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((result=connectserverbyip_nb_auto(sock, ip_addr, port, \
|
||||||
|
connect_timeout)) != 0)
|
||||||
|
{
|
||||||
|
close(sock);
|
||||||
|
|
||||||
|
sprintf(error_info, "file: "__FILE__", line: %d, " \
|
||||||
|
"connect to %s:%d fail, errno: %d, " \
|
||||||
|
"error info: %s", __LINE__, domain_name, \
|
||||||
|
port, result, STRERROR(result));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
out_len = snprintf(out_buff, sizeof(out_buff), \
|
||||||
|
"GET %s HTTP/1.0\r\n" \
|
||||||
|
"Host: %s:%d\r\n" \
|
||||||
|
"Connection: close\r\n" \
|
||||||
|
"\r\n", pURI, domain_name, port);
|
||||||
|
if ((result=tcpsenddata(sock, out_buff, out_len, network_timeout)) != 0)
|
||||||
|
{
|
||||||
|
close(sock);
|
||||||
|
|
||||||
|
sprintf(error_info, "file: "__FILE__", line: %d, " \
|
||||||
|
"send data to %s:%d fail, errno: %d, " \
|
||||||
|
"error info: %s", __LINE__, domain_name, \
|
||||||
|
port, result, STRERROR(result));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
alloc_size = 64 * 1024;
|
||||||
|
*content = (char *)malloc(alloc_size + 1);
|
||||||
|
if (*content == NULL)
|
||||||
|
{
|
||||||
|
close(sock);
|
||||||
|
result = errno != 0 ? errno : ENOMEM;
|
||||||
|
|
||||||
|
sprintf(error_info, "file: "__FILE__", line: %d, " \
|
||||||
|
"malloc %d bytes fail, errno: %d, " \
|
||||||
|
"error info: %s", __LINE__, alloc_size + 1, \
|
||||||
|
result, STRERROR(result));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
recv_bytes = alloc_size - *content_len;
|
||||||
|
if (recv_bytes <= 0)
|
||||||
|
{
|
||||||
|
alloc_size *= 2;
|
||||||
|
*content = (char *)realloc(*content, alloc_size + 1);
|
||||||
|
if (*content == NULL)
|
||||||
|
{
|
||||||
|
close(sock);
|
||||||
|
result = errno != 0 ? errno : ENOMEM;
|
||||||
|
|
||||||
|
sprintf(error_info, "file: "__FILE__", line: %d, " \
|
||||||
|
"realloc %d bytes fail, errno: %d, " \
|
||||||
|
"error info: %s", __LINE__, \
|
||||||
|
alloc_size + 1, \
|
||||||
|
result, STRERROR(result));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
recv_bytes = alloc_size - *content_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = tcprecvdata_ex(sock, *content + *content_len, \
|
||||||
|
recv_bytes, network_timeout, &recv_bytes);
|
||||||
|
|
||||||
|
*content_len += recv_bytes;
|
||||||
|
} while (result == 0);
|
||||||
|
|
||||||
|
if (result != ENOTCONN)
|
||||||
|
{
|
||||||
|
close(sock);
|
||||||
|
free(*content);
|
||||||
|
*content = NULL;
|
||||||
|
*content_len = 0;
|
||||||
|
|
||||||
|
sprintf(error_info, "file: "__FILE__", line: %d, " \
|
||||||
|
"recv data from %s:%d fail, errno: %d, " \
|
||||||
|
"error info: %s", __LINE__, domain_name, \
|
||||||
|
port, result, STRERROR(result));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
*(*content + *content_len) = '\0';
|
||||||
|
pContent = strstr(*content, "\r\n\r\n");
|
||||||
|
if (pContent == NULL)
|
||||||
|
{
|
||||||
|
close(sock);
|
||||||
|
free(*content);
|
||||||
|
*content = NULL;
|
||||||
|
*content_len = 0;
|
||||||
|
|
||||||
|
sprintf(error_info, "file: "__FILE__", line: %d, " \
|
||||||
|
"response data from %s:%d is invalid", \
|
||||||
|
__LINE__, domain_name, port);
|
||||||
|
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
pContent += 4;
|
||||||
|
pSpace = strchr(*content, ' ');
|
||||||
|
if (pSpace == NULL || pSpace >= pContent)
|
||||||
|
{
|
||||||
|
close(sock);
|
||||||
|
free(*content);
|
||||||
|
*content = NULL;
|
||||||
|
*content_len = 0;
|
||||||
|
|
||||||
|
sprintf(error_info, "file: "__FILE__", line: %d, " \
|
||||||
|
"response data from %s:%d is invalid", \
|
||||||
|
__LINE__, domain_name, port);
|
||||||
|
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
*http_status = atoi(pSpace + 1);
|
||||||
|
*content_len -= pContent - *content;
|
||||||
|
memcpy(*content, pContent, *content_len);
|
||||||
|
*(*content + *content_len) = '\0';
|
||||||
|
|
||||||
|
close(sock);
|
||||||
|
|
||||||
|
*error_info = '\0';
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int http_parse_query(char *url, KeyValuePair *params, const int max_count)
|
||||||
|
{
|
||||||
|
KeyValuePair *pCurrent;
|
||||||
|
KeyValuePair *pEnd;
|
||||||
|
char *pParamStart;
|
||||||
|
char *p;
|
||||||
|
char *pStrEnd;
|
||||||
|
int value_len;
|
||||||
|
|
||||||
|
pParamStart = strchr(url, '?');
|
||||||
|
if (pParamStart == NULL)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
*pParamStart = '\0';
|
||||||
|
|
||||||
|
pEnd = params + max_count;
|
||||||
|
pCurrent = params;
|
||||||
|
p = pParamStart + 1;
|
||||||
|
while (p != NULL && *p != '\0')
|
||||||
|
{
|
||||||
|
if (pCurrent >= pEnd)
|
||||||
|
{
|
||||||
|
return pCurrent - params;
|
||||||
|
}
|
||||||
|
|
||||||
|
pCurrent->key = p;
|
||||||
|
pStrEnd = strchr(p, '&');
|
||||||
|
if (pStrEnd == NULL)
|
||||||
|
{
|
||||||
|
p = NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*pStrEnd = '\0';
|
||||||
|
p = pStrEnd + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pStrEnd = strchr(pCurrent->key, '=');
|
||||||
|
if (pStrEnd == NULL)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
*pStrEnd = '\0';
|
||||||
|
pCurrent->value = pStrEnd + 1;
|
||||||
|
if (*pCurrent->key == '\0')
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
urldecode(pCurrent->value, strlen(pCurrent->value), \
|
||||||
|
pCurrent->value, &value_len);
|
||||||
|
pCurrent++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return pCurrent - params;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2008 Happy Fish / YuQing
|
||||||
|
*
|
||||||
|
* FastDFS may be copied only under the terms of the GNU General
|
||||||
|
* Public License V3, which may be found in the FastDFS source kit.
|
||||||
|
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||||
|
**/
|
||||||
|
|
||||||
|
#ifndef _HTTP_FUNC_H
|
||||||
|
#define _HTTP_FUNC_H
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include "common_define.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
get content from url
|
||||||
|
params:
|
||||||
|
url: the url to fetch, must start as: "http://"
|
||||||
|
connect_timeout: connect timeout (seconds)
|
||||||
|
network_timeout: network timeout (seconds)
|
||||||
|
http_status: return http status code, 200 for Ok
|
||||||
|
content: return the content (HTTP body only, not including HTTP header),
|
||||||
|
*content should be freed by caller
|
||||||
|
content_len: return content length (bytes)
|
||||||
|
return: 0 for success, != 0 for error
|
||||||
|
**/
|
||||||
|
int get_url_content(const char *url, const int connect_timeout, \
|
||||||
|
const int network_timeout, int *http_status, \
|
||||||
|
char **content, int *content_len, char *error_info);
|
||||||
|
|
||||||
|
/**
|
||||||
|
parse url
|
||||||
|
params:
|
||||||
|
url: the url to parse
|
||||||
|
params: params array to store param and it's value
|
||||||
|
max_count: max param count
|
||||||
|
return: param count
|
||||||
|
**/
|
||||||
|
int http_parse_query(char *url, KeyValuePair *params, const int max_count);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
@ -0,0 +1,784 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2008 Happy Fish / YuQing
|
||||||
|
*
|
||||||
|
* FastDFS may be copied only under the terms of the GNU General
|
||||||
|
* Public License V3, which may be found in the FastDFS source kit.
|
||||||
|
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||||
|
**/
|
||||||
|
|
||||||
|
//ini_file_reader.c
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include "shared_func.h"
|
||||||
|
#include "logger.h"
|
||||||
|
#include "http_func.h"
|
||||||
|
#include "ini_file_reader.h"
|
||||||
|
|
||||||
|
#define _LINE_BUFFER_SIZE 512
|
||||||
|
#define _ALLOC_ITEMS_ONCE 8
|
||||||
|
|
||||||
|
static int iniDoLoadFromFile(const char *szFilename, \
|
||||||
|
IniContext *pContext);
|
||||||
|
static int iniDoLoadItemsFromBuffer(char *content, \
|
||||||
|
IniContext *pContext);
|
||||||
|
|
||||||
|
static int iniCompareByItemName(const void *p1, const void *p2)
|
||||||
|
{
|
||||||
|
return strcmp(((IniItem *)p1)->name, ((IniItem *)p2)->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int iniInitContext(IniContext *pContext)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
|
||||||
|
memset(pContext, 0, sizeof(IniContext));
|
||||||
|
pContext->current_section = &pContext->global;
|
||||||
|
if ((result=hash_init(&pContext->sections, PJWHash, 10, 0.75)) != 0)
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"hash_init fail, errno: %d, error info: %s", \
|
||||||
|
__LINE__, result, STRERROR(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int iniSortHashData(const int index, const HashData *data, void *args)
|
||||||
|
{
|
||||||
|
IniSection *pSection;
|
||||||
|
|
||||||
|
pSection = (IniSection *)data->value;
|
||||||
|
if (pSection->count > 1)
|
||||||
|
{
|
||||||
|
qsort(pSection->items, pSection->count, \
|
||||||
|
sizeof(IniItem), iniCompareByItemName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void iniSortItems(IniContext *pContext)
|
||||||
|
{
|
||||||
|
if (pContext->global.count > 1)
|
||||||
|
{
|
||||||
|
qsort(pContext->global.items, pContext->global.count, \
|
||||||
|
sizeof(IniItem), iniCompareByItemName);
|
||||||
|
}
|
||||||
|
|
||||||
|
hash_walk(&pContext->sections, iniSortHashData, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
int iniLoadFromFile(const char *szFilename, IniContext *pContext)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
int len;
|
||||||
|
char *pLast;
|
||||||
|
char full_filename[MAX_PATH_SIZE];
|
||||||
|
|
||||||
|
if ((result=iniInitContext(pContext)) != 0)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strncasecmp(szFilename, "http://", 7) == 0)
|
||||||
|
{
|
||||||
|
*pContext->config_path = '\0';
|
||||||
|
snprintf(full_filename, sizeof(full_filename),"%s",szFilename);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (*szFilename == '/')
|
||||||
|
{
|
||||||
|
pLast = strrchr(szFilename, '/');
|
||||||
|
len = pLast - szFilename;
|
||||||
|
if (len >= sizeof(pContext->config_path))
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, "\
|
||||||
|
"the path of the config file: %s is " \
|
||||||
|
"too long!", __LINE__, szFilename);
|
||||||
|
return ENOSPC;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(pContext->config_path, szFilename, len);
|
||||||
|
*(pContext->config_path + len) = '\0';
|
||||||
|
snprintf(full_filename, sizeof(full_filename), \
|
||||||
|
"%s", szFilename);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
memset(pContext->config_path, 0, \
|
||||||
|
sizeof(pContext->config_path));
|
||||||
|
if (getcwd(pContext->config_path, sizeof( \
|
||||||
|
pContext->config_path)) == NULL)
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"getcwd fail, errno: %d, " \
|
||||||
|
"error info: %s", \
|
||||||
|
__LINE__, errno, STRERROR(errno));
|
||||||
|
return errno != 0 ? errno : EPERM;
|
||||||
|
}
|
||||||
|
|
||||||
|
len = strlen(pContext->config_path);
|
||||||
|
if (len > 0 && pContext->config_path[len - 1] == '/')
|
||||||
|
{
|
||||||
|
len--;
|
||||||
|
*(pContext->config_path + len) = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(full_filename, sizeof(full_filename), \
|
||||||
|
"%s/%s", pContext->config_path, szFilename);
|
||||||
|
|
||||||
|
pLast = strrchr(szFilename, '/');
|
||||||
|
if (pLast != NULL)
|
||||||
|
{
|
||||||
|
int tail_len;
|
||||||
|
|
||||||
|
tail_len = pLast - szFilename;
|
||||||
|
if (len + tail_len >= sizeof( \
|
||||||
|
pContext->config_path))
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, "\
|
||||||
|
"the path of the config " \
|
||||||
|
"file: %s is too long!", \
|
||||||
|
__LINE__, szFilename);
|
||||||
|
return ENOSPC;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(pContext->config_path + len, \
|
||||||
|
szFilename, tail_len);
|
||||||
|
len += tail_len;
|
||||||
|
*(pContext->config_path + len) = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result = iniDoLoadFromFile(full_filename, pContext);
|
||||||
|
if (result == 0)
|
||||||
|
{
|
||||||
|
iniSortItems(pContext);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
iniFreeContext(pContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int iniDoLoadFromFile(const char *szFilename, \
|
||||||
|
IniContext *pContext)
|
||||||
|
{
|
||||||
|
char *content;
|
||||||
|
int result;
|
||||||
|
int http_status;
|
||||||
|
int content_len;
|
||||||
|
int64_t file_size;
|
||||||
|
char error_info[512];
|
||||||
|
|
||||||
|
if (strncasecmp(szFilename, "http://", 7) == 0)
|
||||||
|
{
|
||||||
|
if ((result=get_url_content(szFilename, 10, 60, &http_status, \
|
||||||
|
&content, &content_len, error_info)) != 0)
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"get_url_content fail, " \
|
||||||
|
"url: %s, error info: %s", \
|
||||||
|
__LINE__, szFilename, error_info);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (http_status != 200)
|
||||||
|
{
|
||||||
|
free(content);
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"HTTP status code: %d != 200, url: %s", \
|
||||||
|
__LINE__, http_status, szFilename);
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ((result=getFileContent(szFilename, &content, \
|
||||||
|
&file_size)) != 0)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result = iniDoLoadItemsFromBuffer(content, pContext);
|
||||||
|
free(content);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int iniLoadFromBuffer(char *content, IniContext *pContext)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
|
||||||
|
if ((result=iniInitContext(pContext)) != 0)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = iniDoLoadItemsFromBuffer(content, pContext);
|
||||||
|
if (result == 0)
|
||||||
|
{
|
||||||
|
iniSortItems(pContext);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
iniFreeContext(pContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int iniDoLoadItemsFromBuffer(char *content, IniContext *pContext)
|
||||||
|
{
|
||||||
|
IniSection *pSection;
|
||||||
|
IniItem *pItem;
|
||||||
|
char *pLine;
|
||||||
|
char *pLastEnd;
|
||||||
|
char *pEqualChar;
|
||||||
|
char *pIncludeFilename;
|
||||||
|
char full_filename[MAX_PATH_SIZE];
|
||||||
|
int nLineLen;
|
||||||
|
int nNameLen;
|
||||||
|
int nValueLen;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
result = 0;
|
||||||
|
pLastEnd = content - 1;
|
||||||
|
pSection = pContext->current_section;
|
||||||
|
if (pSection->count > 0)
|
||||||
|
{
|
||||||
|
pItem = pSection->items + pSection->count;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pItem = pSection->items;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (pLastEnd != NULL)
|
||||||
|
{
|
||||||
|
pLine = pLastEnd + 1;
|
||||||
|
pLastEnd = strchr(pLine, '\n');
|
||||||
|
if (pLastEnd != NULL)
|
||||||
|
{
|
||||||
|
*pLastEnd = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*pLine == '#' && \
|
||||||
|
strncasecmp(pLine+1, "include", 7) == 0 && \
|
||||||
|
(*(pLine+8) == ' ' || *(pLine+8) == '\t'))
|
||||||
|
{
|
||||||
|
pIncludeFilename = strdup(pLine + 9);
|
||||||
|
if (pIncludeFilename == NULL)
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"strdup %d bytes fail", __LINE__, \
|
||||||
|
(int)strlen(pLine + 9) + 1);
|
||||||
|
result = errno != 0 ? errno : ENOMEM;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
trim(pIncludeFilename);
|
||||||
|
if (strncasecmp(pIncludeFilename, "http://", 7) == 0)
|
||||||
|
{
|
||||||
|
snprintf(full_filename, sizeof(full_filename),\
|
||||||
|
"%s", pIncludeFilename);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (*pIncludeFilename == '/')
|
||||||
|
{
|
||||||
|
snprintf(full_filename, sizeof(full_filename), \
|
||||||
|
"%s", pIncludeFilename);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
snprintf(full_filename, sizeof(full_filename), \
|
||||||
|
"%s/%s", pContext->config_path, \
|
||||||
|
pIncludeFilename);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fileExists(full_filename))
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"include file \"%s\" not exists, " \
|
||||||
|
"line: \"%s\"", __LINE__, \
|
||||||
|
pIncludeFilename, pLine);
|
||||||
|
free(pIncludeFilename);
|
||||||
|
result = ENOENT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result = iniDoLoadFromFile(full_filename, pContext);
|
||||||
|
if (result != 0)
|
||||||
|
{
|
||||||
|
free(pIncludeFilename);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
pSection = pContext->current_section;
|
||||||
|
if (pSection->count > 0)
|
||||||
|
{
|
||||||
|
pItem = pSection->items + pSection->count; //must re-asign
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pItem = pSection->items;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(pIncludeFilename);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
trim(pLine);
|
||||||
|
if (*pLine == '#' || *pLine == '\0')
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
nLineLen = strlen(pLine);
|
||||||
|
if (*pLine == '[' && *(pLine + (nLineLen - 1)) == ']') //section
|
||||||
|
{
|
||||||
|
char *section_name;
|
||||||
|
int section_len;
|
||||||
|
|
||||||
|
*(pLine + (nLineLen - 1)) = '\0';
|
||||||
|
section_name = pLine + 1; //skip [
|
||||||
|
|
||||||
|
trim(section_name);
|
||||||
|
if (*section_name == '\0') //global section
|
||||||
|
{
|
||||||
|
pContext->current_section = &pContext->global;
|
||||||
|
pSection = pContext->current_section;
|
||||||
|
if (pSection->count > 0)
|
||||||
|
{
|
||||||
|
pItem = pSection->items + pSection->count;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pItem = pSection->items;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
section_len = strlen(section_name);
|
||||||
|
pSection = (IniSection *)hash_find(&pContext->sections,\
|
||||||
|
section_name, section_len);
|
||||||
|
if (pSection == NULL)
|
||||||
|
{
|
||||||
|
pSection = (IniSection *)malloc(sizeof(IniSection));
|
||||||
|
if (pSection == NULL)
|
||||||
|
{
|
||||||
|
result = errno != 0 ? errno : ENOMEM;
|
||||||
|
logError("file: "__FILE__", line: %d, "\
|
||||||
|
"malloc %d bytes fail, " \
|
||||||
|
"errno: %d, error info: %s", \
|
||||||
|
__LINE__, \
|
||||||
|
(int)sizeof(IniSection), \
|
||||||
|
result, STRERROR(result));
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(pSection, 0, sizeof(IniSection));
|
||||||
|
result = hash_insert(&pContext->sections, \
|
||||||
|
section_name, section_len, pSection);
|
||||||
|
if (result < 0)
|
||||||
|
{
|
||||||
|
result *= -1;
|
||||||
|
logError("file: "__FILE__", line: %d, "\
|
||||||
|
"insert into hash table fail, "\
|
||||||
|
"errno: %d, error info: %s", \
|
||||||
|
__LINE__, result, \
|
||||||
|
STRERROR(result));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pContext->current_section = pSection;
|
||||||
|
if (pSection->count > 0)
|
||||||
|
{
|
||||||
|
pItem = pSection->items + pSection->count;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pItem = pSection->items;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
pEqualChar = strchr(pLine, '=');
|
||||||
|
if (pEqualChar == NULL)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
nNameLen = pEqualChar - pLine;
|
||||||
|
nValueLen = strlen(pLine) - (nNameLen + 1);
|
||||||
|
if (nNameLen > FAST_INI_ITEM_NAME_LEN)
|
||||||
|
{
|
||||||
|
nNameLen = FAST_INI_ITEM_NAME_LEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nValueLen > FAST_INI_ITEM_VALUE_LEN)
|
||||||
|
{
|
||||||
|
nValueLen = FAST_INI_ITEM_VALUE_LEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pSection->count >= pSection->alloc_count)
|
||||||
|
{
|
||||||
|
pSection->alloc_count += _ALLOC_ITEMS_ONCE;
|
||||||
|
pSection->items=(IniItem *)realloc(pSection->items,
|
||||||
|
sizeof(IniItem) * pSection->alloc_count);
|
||||||
|
if (pSection->items == NULL)
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"realloc %d bytes fail", __LINE__, \
|
||||||
|
(int)sizeof(IniItem) * \
|
||||||
|
pSection->alloc_count);
|
||||||
|
result = errno != 0 ? errno : ENOMEM;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
pItem = pSection->items + pSection->count;
|
||||||
|
memset(pItem, 0, sizeof(IniItem) * \
|
||||||
|
(pSection->alloc_count - pSection->count));
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(pItem->name, pLine, nNameLen);
|
||||||
|
memcpy(pItem->value, pEqualChar + 1, nValueLen);
|
||||||
|
|
||||||
|
trim(pItem->name);
|
||||||
|
trim(pItem->value);
|
||||||
|
|
||||||
|
pSection->count++;
|
||||||
|
pItem++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int iniFreeHashData(const int index, const HashData *data, void *args)
|
||||||
|
{
|
||||||
|
IniSection *pSection;
|
||||||
|
|
||||||
|
pSection = (IniSection *)data->value;
|
||||||
|
if (pSection == NULL)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pSection->items != NULL)
|
||||||
|
{
|
||||||
|
free(pSection->items);
|
||||||
|
memset(pSection, 0, sizeof(IniSection));
|
||||||
|
}
|
||||||
|
|
||||||
|
free(pSection);
|
||||||
|
((HashData *)data)->value = NULL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void iniFreeContext(IniContext *pContext)
|
||||||
|
{
|
||||||
|
if (pContext == NULL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pContext->global.items != NULL)
|
||||||
|
{
|
||||||
|
free(pContext->global.items);
|
||||||
|
memset(&pContext->global, 0, sizeof(IniSection));
|
||||||
|
}
|
||||||
|
|
||||||
|
hash_walk(&pContext->sections, iniFreeHashData, NULL);
|
||||||
|
hash_destroy(&pContext->sections);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#define INI_FIND_ITEM(szSectionName, szItemName, pContext, pSection, \
|
||||||
|
targetItem, pItem, return_val) \
|
||||||
|
if (szSectionName == NULL || *szSectionName == '\0') \
|
||||||
|
{ \
|
||||||
|
pSection = &pContext->global; \
|
||||||
|
} \
|
||||||
|
else \
|
||||||
|
{ \
|
||||||
|
pSection = (IniSection *)hash_find(&pContext->sections, \
|
||||||
|
szSectionName, strlen(szSectionName)); \
|
||||||
|
if (pSection == NULL) \
|
||||||
|
{ \
|
||||||
|
return return_val; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
if (pSection->count <= 0) \
|
||||||
|
{ \
|
||||||
|
return return_val; \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
snprintf(targetItem.name, sizeof(targetItem.name), "%s", szItemName); \
|
||||||
|
pItem = (IniItem *)bsearch(&targetItem, pSection->items, \
|
||||||
|
pSection->count, sizeof(IniItem), iniCompareByItemName);
|
||||||
|
|
||||||
|
|
||||||
|
char *iniGetStrValue(const char *szSectionName, const char *szItemName, \
|
||||||
|
IniContext *pContext)
|
||||||
|
{
|
||||||
|
IniItem targetItem;
|
||||||
|
IniSection *pSection;
|
||||||
|
IniItem *pItem;
|
||||||
|
|
||||||
|
INI_FIND_ITEM(szSectionName, szItemName, pContext, pSection, \
|
||||||
|
targetItem, pItem, NULL)
|
||||||
|
|
||||||
|
if (pItem == NULL)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return pItem->value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t iniGetInt64Value(const char *szSectionName, const char *szItemName, \
|
||||||
|
IniContext *pContext, const int64_t nDefaultValue)
|
||||||
|
{
|
||||||
|
char *pValue;
|
||||||
|
|
||||||
|
pValue = iniGetStrValue(szSectionName, szItemName, pContext);
|
||||||
|
if (pValue == NULL)
|
||||||
|
{
|
||||||
|
return nDefaultValue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return strtoll(pValue, NULL, 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int iniGetIntValue(const char *szSectionName, const char *szItemName, \
|
||||||
|
IniContext *pContext, const int nDefaultValue)
|
||||||
|
{
|
||||||
|
char *pValue;
|
||||||
|
|
||||||
|
pValue = iniGetStrValue(szSectionName, szItemName, pContext);
|
||||||
|
if (pValue == NULL)
|
||||||
|
{
|
||||||
|
return nDefaultValue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return atoi(pValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double iniGetDoubleValue(const char *szSectionName, const char *szItemName, \
|
||||||
|
IniContext *pContext, const double dbDefaultValue)
|
||||||
|
{
|
||||||
|
char *pValue;
|
||||||
|
|
||||||
|
pValue = iniGetStrValue(szSectionName, szItemName, pContext);
|
||||||
|
if (pValue == NULL)
|
||||||
|
{
|
||||||
|
return dbDefaultValue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return strtod(pValue, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool iniGetBoolValue(const char *szSectionName, const char *szItemName, \
|
||||||
|
IniContext *pContext, const bool bDefaultValue)
|
||||||
|
{
|
||||||
|
char *pValue;
|
||||||
|
|
||||||
|
pValue = iniGetStrValue(szSectionName, szItemName, pContext);
|
||||||
|
if (pValue == NULL)
|
||||||
|
{
|
||||||
|
return bDefaultValue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return strcasecmp(pValue, "true") == 0 ||
|
||||||
|
strcasecmp(pValue, "yes") == 0 ||
|
||||||
|
strcasecmp(pValue, "on") == 0 ||
|
||||||
|
strcmp(pValue, "1") == 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int iniGetValues(const char *szSectionName, const char *szItemName, \
|
||||||
|
IniContext *pContext, char **szValues, const int max_values)
|
||||||
|
{
|
||||||
|
IniItem targetItem;
|
||||||
|
IniSection *pSection;
|
||||||
|
IniItem *pFound;
|
||||||
|
IniItem *pItem;
|
||||||
|
IniItem *pItemEnd;
|
||||||
|
char **ppValues;
|
||||||
|
|
||||||
|
if (max_values <= 0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
INI_FIND_ITEM(szSectionName, szItemName, pContext, pSection, \
|
||||||
|
targetItem, pFound, 0)
|
||||||
|
if (pFound == NULL)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ppValues = szValues;
|
||||||
|
*ppValues++ = pFound->value;
|
||||||
|
for (pItem=pFound-1; pItem>=pSection->items; pItem--)
|
||||||
|
{
|
||||||
|
if (strcmp(pItem->name, szItemName) != 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ppValues - szValues < max_values)
|
||||||
|
{
|
||||||
|
*ppValues++ = pItem->value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pItemEnd = pSection->items + pSection->count;
|
||||||
|
for (pItem=pFound+1; pItem<pItemEnd; pItem++)
|
||||||
|
{
|
||||||
|
if (strcmp(pItem->name, szItemName) != 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ppValues - szValues < max_values)
|
||||||
|
{
|
||||||
|
*ppValues++ = pItem->value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ppValues - szValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
IniItem *iniGetValuesEx(const char *szSectionName, const char *szItemName, \
|
||||||
|
IniContext *pContext, int *nTargetCount)
|
||||||
|
{
|
||||||
|
IniItem targetItem;
|
||||||
|
IniSection *pSection;
|
||||||
|
IniItem *pFound;
|
||||||
|
IniItem *pItem;
|
||||||
|
IniItem *pItemEnd;
|
||||||
|
IniItem *pItemStart;
|
||||||
|
|
||||||
|
*nTargetCount = 0;
|
||||||
|
INI_FIND_ITEM(szSectionName, szItemName, pContext, pSection, \
|
||||||
|
targetItem, pFound, NULL)
|
||||||
|
if (pFound == NULL)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
*nTargetCount = 1;
|
||||||
|
for (pItem=pFound-1; pItem>=pSection->items; pItem--)
|
||||||
|
{
|
||||||
|
if (strcmp(pItem->name, szItemName) != 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
(*nTargetCount)++;
|
||||||
|
}
|
||||||
|
pItemStart = pFound - (*nTargetCount) + 1;
|
||||||
|
|
||||||
|
pItemEnd = pSection->items + pSection->count;
|
||||||
|
for (pItem=pFound+1; pItem<pItemEnd; pItem++)
|
||||||
|
{
|
||||||
|
if (strcmp(pItem->name, szItemName) != 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
(*nTargetCount)++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return pItemStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int iniPrintHashData(const int index, const HashData *data, void *args)
|
||||||
|
{
|
||||||
|
IniSection *pSection;
|
||||||
|
IniItem *pItem;
|
||||||
|
IniItem *pItemEnd;
|
||||||
|
char section_name[256];
|
||||||
|
int section_len;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
pSection = (IniSection *)data->value;
|
||||||
|
if (pSection == NULL)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
section_len = data->key_len;
|
||||||
|
if (section_len >= sizeof(section_name))
|
||||||
|
{
|
||||||
|
section_len = sizeof(section_name) - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(section_name, data->key, section_len);
|
||||||
|
*(section_name + section_len) = '\0';
|
||||||
|
|
||||||
|
printf("section: %s, item count: %d\n", section_name, pSection->count);
|
||||||
|
if (pSection->count > 0)
|
||||||
|
{
|
||||||
|
i = 0;
|
||||||
|
pItemEnd = pSection->items + pSection->count;
|
||||||
|
for (pItem=pSection->items; pItem<pItemEnd; pItem++)
|
||||||
|
{
|
||||||
|
printf("%d. %s=%s\n", ++i, pItem->name, pItem->value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void iniPrintItems(IniContext *pContext)
|
||||||
|
{
|
||||||
|
IniItem *pItem;
|
||||||
|
IniItem *pItemEnd;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
printf("global section, item count: %d\n", pContext->global.count);
|
||||||
|
if (pContext->global.count > 0)
|
||||||
|
{
|
||||||
|
i = 0;
|
||||||
|
pItemEnd = pContext->global.items + pContext->global.count;
|
||||||
|
for (pItem=pContext->global.items; pItem<pItemEnd; pItem++)
|
||||||
|
{
|
||||||
|
printf("%d. %s=%s\n", ++i, pItem->name, pItem->value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
hash_walk(&pContext->sections, iniPrintHashData, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,166 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2008 Happy Fish / YuQing
|
||||||
|
*
|
||||||
|
* FastDFS may be copied only under the terms of the GNU General
|
||||||
|
* Public License V3, which may be found in the FastDFS source kit.
|
||||||
|
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||||
|
**/
|
||||||
|
|
||||||
|
//ini_file_reader.h
|
||||||
|
#ifndef INI_FILE_READER_H
|
||||||
|
#define INI_FILE_READER_H
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "common_define.h"
|
||||||
|
#include "hash.h"
|
||||||
|
|
||||||
|
#define FAST_INI_ITEM_NAME_LEN 64
|
||||||
|
#define FAST_INI_ITEM_VALUE_LEN 256
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
char name[FAST_INI_ITEM_NAME_LEN + 1];
|
||||||
|
char value[FAST_INI_ITEM_VALUE_LEN + 1];
|
||||||
|
} IniItem;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
IniItem *items;
|
||||||
|
int count; //item count
|
||||||
|
int alloc_count;
|
||||||
|
} IniSection;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
IniSection global;
|
||||||
|
HashArray sections; //key is session name, and value is IniSection
|
||||||
|
IniSection *current_section; //for load from ini file
|
||||||
|
char config_path[MAX_PATH_SIZE]; //save the config filepath
|
||||||
|
} IniContext;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** load ini items from file
|
||||||
|
* parameters:
|
||||||
|
* szFilename: the filename, can be an URL
|
||||||
|
* pContext: the ini context
|
||||||
|
* return: error no, 0 for success, != 0 fail
|
||||||
|
*/
|
||||||
|
int iniLoadFromFile(const char *szFilename, IniContext *pContext);
|
||||||
|
|
||||||
|
/** load ini items from string buffer
|
||||||
|
* parameters:
|
||||||
|
* content: the string buffer to parse
|
||||||
|
* pContext: the ini context
|
||||||
|
* return: error no, 0 for success, != 0 fail
|
||||||
|
*/
|
||||||
|
int iniLoadFromBuffer(char *content, IniContext *pContext);
|
||||||
|
|
||||||
|
/** free ini context
|
||||||
|
* parameters:
|
||||||
|
* pContext: the ini context
|
||||||
|
* return: none
|
||||||
|
*/
|
||||||
|
void iniFreeContext(IniContext *pContext);
|
||||||
|
|
||||||
|
/** get item string value
|
||||||
|
* parameters:
|
||||||
|
* szSectionName: the section name, NULL or empty string for
|
||||||
|
* global section
|
||||||
|
* szItemName: the item name
|
||||||
|
* pContext: the ini context
|
||||||
|
* return: item value, return NULL when the item not exist
|
||||||
|
*/
|
||||||
|
char *iniGetStrValue(const char *szSectionName, const char *szItemName, \
|
||||||
|
IniContext *pContext);
|
||||||
|
|
||||||
|
/** get item string value
|
||||||
|
* parameters:
|
||||||
|
* szSectionName: the section name, NULL or empty string for
|
||||||
|
* global section
|
||||||
|
* szItemName: the item name
|
||||||
|
* pContext: the ini context
|
||||||
|
* szValues: string array to store the values
|
||||||
|
* max_values: max string array elements
|
||||||
|
* return: item value count
|
||||||
|
*/
|
||||||
|
int iniGetValues(const char *szSectionName, const char *szItemName, \
|
||||||
|
IniContext *pContext, char **szValues, const int max_values);
|
||||||
|
|
||||||
|
/** get item int value (32 bits)
|
||||||
|
* parameters:
|
||||||
|
* szSectionName: the section name, NULL or empty string for
|
||||||
|
* global section
|
||||||
|
* szItemName: the item name
|
||||||
|
* pContext: the ini context
|
||||||
|
* nDefaultValue: the default value
|
||||||
|
* return: item value, return nDefaultValue when the item not exist
|
||||||
|
*/
|
||||||
|
int iniGetIntValue(const char *szSectionName, const char *szItemName, \
|
||||||
|
IniContext *pContext, const int nDefaultValue);
|
||||||
|
|
||||||
|
/** get item string value array
|
||||||
|
* parameters:
|
||||||
|
* szSectionName: the section name, NULL or empty string for
|
||||||
|
* global section
|
||||||
|
* szItemName: the item name
|
||||||
|
* pContext: the ini context
|
||||||
|
* nTargetCount: store the item value count
|
||||||
|
* return: item value array, return NULL when the item not exist
|
||||||
|
*/
|
||||||
|
IniItem *iniGetValuesEx(const char *szSectionName, const char *szItemName, \
|
||||||
|
IniContext *pContext, int *nTargetCount);
|
||||||
|
|
||||||
|
/** get item int64 value (64 bits)
|
||||||
|
* parameters:
|
||||||
|
* szSectionName: the section name, NULL or empty string for
|
||||||
|
* global section
|
||||||
|
* szItemName: the item name
|
||||||
|
* pContext: the ini context
|
||||||
|
* nDefaultValue: the default value
|
||||||
|
* return: int64 value, return nDefaultValue when the item not exist
|
||||||
|
*/
|
||||||
|
int64_t iniGetInt64Value(const char *szSectionName, const char *szItemName, \
|
||||||
|
IniContext *pContext, const int64_t nDefaultValue);
|
||||||
|
|
||||||
|
/** get item boolean value
|
||||||
|
* parameters:
|
||||||
|
* szSectionName: the section name, NULL or empty string for
|
||||||
|
* global section
|
||||||
|
* szItemName: the item name
|
||||||
|
* pContext: the ini context
|
||||||
|
* bDefaultValue: the default value
|
||||||
|
* return: item boolean value, return bDefaultValue when the item not exist
|
||||||
|
*/
|
||||||
|
bool iniGetBoolValue(const char *szSectionName, const char *szItemName, \
|
||||||
|
IniContext *pContext, const bool bDefaultValue);
|
||||||
|
|
||||||
|
/** get item double value
|
||||||
|
* parameters:
|
||||||
|
* szSectionName: the section name, NULL or empty string for
|
||||||
|
* global section
|
||||||
|
* szItemName: the item name
|
||||||
|
* pContext: the ini context
|
||||||
|
* dbDefaultValue: the default value
|
||||||
|
* return: item value, return dbDefaultValue when the item not exist
|
||||||
|
*/
|
||||||
|
double iniGetDoubleValue(const char *szSectionName, const char *szItemName, \
|
||||||
|
IniContext *pContext, const double dbDefaultValue);
|
||||||
|
|
||||||
|
/** print all items
|
||||||
|
* parameters:
|
||||||
|
* pContext: the ini context
|
||||||
|
* return: none
|
||||||
|
*/
|
||||||
|
void iniPrintItems(IniContext *pContext);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
@ -0,0 +1,179 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include "ioevent.h"
|
||||||
|
|
||||||
|
#if IOEVENT_USE_KQUEUE
|
||||||
|
/* we define these here as numbers, because for kqueue mapping them to a combination of
|
||||||
|
* filters / flags is hard to do. */
|
||||||
|
int kqueue_ev_convert(int16_t event, uint16_t flags)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
|
||||||
|
if (event == EVFILT_READ) {
|
||||||
|
r = KPOLLIN;
|
||||||
|
}
|
||||||
|
else if (event == EVFILT_WRITE) {
|
||||||
|
r = KPOLLOUT;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
r = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags & EV_EOF) {
|
||||||
|
r |= KPOLLHUP;
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int ioevent_init(IOEventPoller *ioevent, const int size,
|
||||||
|
const int timeout, const int extra_events)
|
||||||
|
{
|
||||||
|
int bytes;
|
||||||
|
|
||||||
|
ioevent->size = size;
|
||||||
|
ioevent->extra_events = extra_events;
|
||||||
|
|
||||||
|
#if IOEVENT_USE_EPOLL
|
||||||
|
ioevent->timeout = timeout;
|
||||||
|
ioevent->poll_fd = epoll_create(ioevent->size);
|
||||||
|
bytes = sizeof(struct epoll_event) * size;
|
||||||
|
ioevent->events = (struct epoll_event *)malloc(bytes);
|
||||||
|
#elif IOEVENT_USE_KQUEUE
|
||||||
|
ioevent->timeout.tv_sec = timeout / 1000;
|
||||||
|
ioevent->timeout.tv_nsec = 1000000 * (timeout % 1000);
|
||||||
|
ioevent->poll_fd = kqueue();
|
||||||
|
bytes = sizeof(struct kevent) * size;
|
||||||
|
ioevent->events = (struct kevent *)malloc(bytes);
|
||||||
|
#elif IOEVENT_USE_PORT
|
||||||
|
ioevent->timeout.tv_sec = timeout / 1000;
|
||||||
|
ioevent->timeout.tv_nsec = 1000000 * (timeout % 1000);
|
||||||
|
ioevent->poll_fd = port_create();
|
||||||
|
bytes = sizeof(port_event_t) * size;
|
||||||
|
ioevent->events = (port_event_t *)malloc(bytes);
|
||||||
|
#endif
|
||||||
|
if (ioevent->events == NULL) {
|
||||||
|
return errno != 0 ? errno : ENOMEM;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ioevent_destroy(IOEventPoller *ioevent)
|
||||||
|
{
|
||||||
|
if (ioevent->events != NULL) {
|
||||||
|
free(ioevent->events);
|
||||||
|
ioevent->events = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ioevent->poll_fd >=0) {
|
||||||
|
close(ioevent->poll_fd);
|
||||||
|
ioevent->poll_fd = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int ioevent_attach(IOEventPoller *ioevent, const int fd, const int e,
|
||||||
|
void *data)
|
||||||
|
{
|
||||||
|
#if IOEVENT_USE_EPOLL
|
||||||
|
struct epoll_event ev;
|
||||||
|
memset(&ev, 0, sizeof(ev));
|
||||||
|
ev.events = e | ioevent->extra_events;
|
||||||
|
ev.data.ptr = data;
|
||||||
|
return epoll_ctl(ioevent->poll_fd, EPOLL_CTL_ADD, fd, &ev);
|
||||||
|
#elif IOEVENT_USE_KQUEUE
|
||||||
|
struct kevent ev[2];
|
||||||
|
int n = 0;
|
||||||
|
if (e & IOEVENT_READ) {
|
||||||
|
EV_SET(&ev[n++], fd, EVFILT_READ, EV_ADD | ioevent->extra_events, 0, 0, data);
|
||||||
|
}
|
||||||
|
if (e & IOEVENT_WRITE) {
|
||||||
|
EV_SET(&ev[n++], fd, EVFILT_WRITE, EV_ADD | ioevent->extra_events, 0, 0, data);
|
||||||
|
}
|
||||||
|
return kevent(ioevent->poll_fd, ev, n, NULL, 0, NULL);
|
||||||
|
#elif IOEVENT_USE_PORT
|
||||||
|
return port_associate(ioevent->poll_fd, PORT_SOURCE_FD, fd, e, data);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int ioevent_modify(IOEventPoller *ioevent, const int fd, const int e,
|
||||||
|
void *data)
|
||||||
|
{
|
||||||
|
#if IOEVENT_USE_EPOLL
|
||||||
|
struct epoll_event ev;
|
||||||
|
memset(&ev, 0, sizeof(ev));
|
||||||
|
ev.events = e | ioevent->extra_events;
|
||||||
|
ev.data.ptr = data;
|
||||||
|
return epoll_ctl(ioevent->poll_fd, EPOLL_CTL_MOD, fd, &ev);
|
||||||
|
#elif IOEVENT_USE_KQUEUE
|
||||||
|
struct kevent ev[2];
|
||||||
|
int n = 0;
|
||||||
|
if (e & IOEVENT_READ) {
|
||||||
|
EV_SET(&ev[n++], fd, EVFILT_READ, EV_ADD | ioevent->extra_events, 0, 0, data);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
EV_SET(&ev[n++], fd, EVFILT_READ, EV_DELETE, 0, 0, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e & IOEVENT_WRITE) {
|
||||||
|
EV_SET(&ev[n++], fd, EVFILT_WRITE, EV_ADD | ioevent->extra_events, 0, 0, data);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
EV_SET(&ev[n++], fd, EVFILT_WRITE, EV_DELETE, 0, 0, data);
|
||||||
|
}
|
||||||
|
return kevent(ioevent->poll_fd, ev, n, NULL, 0, NULL);
|
||||||
|
#elif IOEVENT_USE_PORT
|
||||||
|
return port_associate(ioevent->poll_fd, PORT_SOURCE_FD, fd, e, data);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int ioevent_detach(IOEventPoller *ioevent, const int fd)
|
||||||
|
{
|
||||||
|
#if IOEVENT_USE_EPOLL
|
||||||
|
return epoll_ctl(ioevent->poll_fd, EPOLL_CTL_DEL, fd, NULL);
|
||||||
|
#elif IOEVENT_USE_PORT
|
||||||
|
return port_dissociate(ioevent->poll_fd, PORT_SOURCE_FD, fd);
|
||||||
|
#else
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int ioevent_poll(IOEventPoller *ioevent)
|
||||||
|
{
|
||||||
|
#if IOEVENT_USE_EPOLL
|
||||||
|
return epoll_wait(ioevent->poll_fd, ioevent->events, ioevent->size, ioevent->timeout);
|
||||||
|
#elif IOEVENT_USE_KQUEUE
|
||||||
|
return kevent(ioevent->poll_fd, NULL, 0, ioevent->events, ioevent->size, &ioevent->timeout);
|
||||||
|
#elif IOEVENT_USE_PORT
|
||||||
|
int result;
|
||||||
|
int retval;
|
||||||
|
unsigned int nget = 1;
|
||||||
|
if((retval = port_getn(ioevent->poll_fd, ioevent->events,
|
||||||
|
ioevent->size, &nget, &ioevent->timeout)) == 0)
|
||||||
|
{
|
||||||
|
result = (int)nget;
|
||||||
|
} else {
|
||||||
|
switch(errno) {
|
||||||
|
case EINTR:
|
||||||
|
case EAGAIN:
|
||||||
|
case ETIME:
|
||||||
|
if (nget > 0) {
|
||||||
|
result = (int)nget;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
result = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
result = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
#else
|
||||||
|
#error port me
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,113 @@
|
||||||
|
#ifndef __IOEVENT_H__
|
||||||
|
#define __IOEVENT_H__
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <poll.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
|
||||||
|
#define IOEVENT_TIMEOUT 0x8000
|
||||||
|
|
||||||
|
#if IOEVENT_USE_EPOLL
|
||||||
|
#include <sys/epoll.h>
|
||||||
|
#define IOEVENT_EDGE_TRIGGER EPOLLET
|
||||||
|
|
||||||
|
#define IOEVENT_READ EPOLLIN
|
||||||
|
#define IOEVENT_WRITE EPOLLOUT
|
||||||
|
#define IOEVENT_ERROR (EPOLLERR | EPOLLPRI | EPOLLHUP)
|
||||||
|
|
||||||
|
#elif IOEVENT_USE_KQUEUE
|
||||||
|
#include <sys/event.h>
|
||||||
|
#define IOEVENT_EDGE_TRIGGER EV_CLEAR
|
||||||
|
|
||||||
|
#define KPOLLIN 0x001
|
||||||
|
#define KPOLLPRI 0x002
|
||||||
|
#define KPOLLOUT 0x004
|
||||||
|
#define KPOLLERR 0x010
|
||||||
|
#define KPOLLHUP 0x020
|
||||||
|
#define IOEVENT_READ KPOLLIN
|
||||||
|
#define IOEVENT_WRITE KPOLLOUT
|
||||||
|
#define IOEVENT_ERROR (KPOLLHUP | KPOLLPRI | KPOLLHUP)
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int kqueue_ev_convert(int16_t event, uint16_t flags);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#elif IOEVENT_USE_PORT
|
||||||
|
#include <port.h>
|
||||||
|
#define IOEVENT_EDGE_TRIGGER 0
|
||||||
|
|
||||||
|
#define IOEVENT_READ POLLIN
|
||||||
|
#define IOEVENT_WRITE POLLOUT
|
||||||
|
#define IOEVENT_ERROR (POLLERR | POLLPRI | POLLHUP)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct ioevent_puller {
|
||||||
|
int size; //max events (fd)
|
||||||
|
int extra_events;
|
||||||
|
int poll_fd;
|
||||||
|
|
||||||
|
#if IOEVENT_USE_EPOLL
|
||||||
|
struct epoll_event *events;
|
||||||
|
int timeout;
|
||||||
|
#elif IOEVENT_USE_KQUEUE
|
||||||
|
struct kevent *events;
|
||||||
|
struct timespec timeout;
|
||||||
|
#elif IOEVENT_USE_PORT
|
||||||
|
port_event_t *events;
|
||||||
|
timespec_t timeout;
|
||||||
|
#endif
|
||||||
|
} IOEventPoller;
|
||||||
|
|
||||||
|
#if IOEVENT_USE_EPOLL
|
||||||
|
#define IOEVENT_GET_EVENTS(ioevent, index) \
|
||||||
|
ioevent->events[index].events
|
||||||
|
#elif IOEVENT_USE_KQUEUE
|
||||||
|
#define IOEVENT_GET_EVENTS(ioevent, index) kqueue_ev_convert( \
|
||||||
|
ioevent->events[index].filter, ioevent->events[index].flags)
|
||||||
|
#elif IOEVENT_USE_PORT
|
||||||
|
#define IOEVENT_GET_EVENTS(ioevent, index) \
|
||||||
|
ioevent->events[index].portev_events
|
||||||
|
#else
|
||||||
|
#error port me
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if IOEVENT_USE_EPOLL
|
||||||
|
#define IOEVENT_GET_DATA(ioevent, index) \
|
||||||
|
ioevent->events[index].data.ptr
|
||||||
|
#elif IOEVENT_USE_KQUEUE
|
||||||
|
#define IOEVENT_GET_DATA(ioevent, index) \
|
||||||
|
ioevent->events[index].udata
|
||||||
|
#elif IOEVENT_USE_PORT
|
||||||
|
#define IOEVENT_GET_DATA(ioevent, index) \
|
||||||
|
ioevent->events[index].portev_user
|
||||||
|
#else
|
||||||
|
#error port me
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int ioevent_init(IOEventPoller *ioevent, const int size,
|
||||||
|
const int timeout, const int extra_events);
|
||||||
|
void ioevent_destroy(IOEventPoller *ioevent);
|
||||||
|
|
||||||
|
int ioevent_attach(IOEventPoller *ioevent, const int fd, const int e,
|
||||||
|
void *data);
|
||||||
|
int ioevent_modify(IOEventPoller *ioevent, const int fd, const int e,
|
||||||
|
void *data);
|
||||||
|
int ioevent_detach(IOEventPoller *ioevent, const int fd);
|
||||||
|
int ioevent_poll(IOEventPoller *ioevent);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
@ -0,0 +1,150 @@
|
||||||
|
#include "sched_thread.h"
|
||||||
|
#include "logger.h"
|
||||||
|
#include "ioevent_loop.h"
|
||||||
|
|
||||||
|
static void deal_ioevents(IOEventPoller *ioevent, const int count)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int event;
|
||||||
|
IOEventEntry *pEntry;
|
||||||
|
for (i=0; i<count; i++)
|
||||||
|
{
|
||||||
|
event = IOEVENT_GET_EVENTS(ioevent, i);
|
||||||
|
pEntry = (IOEventEntry *)IOEVENT_GET_DATA(ioevent, i);
|
||||||
|
|
||||||
|
pEntry->callback(pEntry->fd, event, pEntry->timer.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void deal_timeouts(FastTimerEntry *head)
|
||||||
|
{
|
||||||
|
FastTimerEntry *entry;
|
||||||
|
FastTimerEntry *curent;
|
||||||
|
IOEventEntry *pEventEntry;
|
||||||
|
|
||||||
|
entry = head->next;
|
||||||
|
while (entry != NULL)
|
||||||
|
{
|
||||||
|
curent = entry;
|
||||||
|
entry = entry->next;
|
||||||
|
|
||||||
|
pEventEntry = (IOEventEntry *)curent->data;
|
||||||
|
if (pEventEntry != NULL)
|
||||||
|
{
|
||||||
|
pEventEntry->callback(pEventEntry->fd, IOEVENT_TIMEOUT,
|
||||||
|
curent->data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int ioevent_loop(struct nio_thread_data *pThreadData,
|
||||||
|
IOEventCallback recv_notify_callback, TaskCleanUpCallBack
|
||||||
|
clean_up_callback, volatile bool *continue_flag)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
IOEventEntry ev_notify;
|
||||||
|
FastTimerEntry head;
|
||||||
|
struct fast_task_info *pTask;
|
||||||
|
time_t last_check_time;
|
||||||
|
int count;
|
||||||
|
|
||||||
|
memset(&ev_notify, 0, sizeof(ev_notify));
|
||||||
|
ev_notify.fd = pThreadData->pipe_fds[0];
|
||||||
|
ev_notify.callback = recv_notify_callback;
|
||||||
|
if (ioevent_attach(&pThreadData->ev_puller,
|
||||||
|
pThreadData->pipe_fds[0], IOEVENT_READ,
|
||||||
|
&ev_notify) != 0)
|
||||||
|
{
|
||||||
|
result = errno != 0 ? errno : ENOMEM;
|
||||||
|
logCrit("file: "__FILE__", line: %d, " \
|
||||||
|
"ioevent_attach fail, " \
|
||||||
|
"errno: %d, error info: %s", \
|
||||||
|
__LINE__, result, STRERROR(result));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
last_check_time = g_current_time;
|
||||||
|
while (*continue_flag)
|
||||||
|
{
|
||||||
|
pThreadData->deleted_list = NULL;
|
||||||
|
count = ioevent_poll(&pThreadData->ev_puller);
|
||||||
|
if (count > 0)
|
||||||
|
{
|
||||||
|
deal_ioevents(&pThreadData->ev_puller, count);
|
||||||
|
}
|
||||||
|
else if (count < 0)
|
||||||
|
{
|
||||||
|
result = errno != 0 ? errno : EINVAL;
|
||||||
|
if (result != EINTR)
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"ioevent_poll fail, " \
|
||||||
|
"errno: %d, error info: %s", \
|
||||||
|
__LINE__, result, STRERROR(result));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pThreadData->deleted_list != NULL)
|
||||||
|
{
|
||||||
|
count = 0;
|
||||||
|
while (pThreadData->deleted_list != NULL)
|
||||||
|
{
|
||||||
|
pTask = pThreadData->deleted_list;
|
||||||
|
pThreadData->deleted_list = pTask->next;
|
||||||
|
|
||||||
|
clean_up_callback(pTask);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
logInfo("cleanup task count: %d", count);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_current_time - last_check_time > 0)
|
||||||
|
{
|
||||||
|
last_check_time = g_current_time;
|
||||||
|
count = fast_timer_timeouts_get(
|
||||||
|
&pThreadData->timer, g_current_time, &head);
|
||||||
|
if (count > 0)
|
||||||
|
{
|
||||||
|
deal_timeouts(&head);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ioevent_set(struct fast_task_info *pTask, struct nio_thread_data *pThread,
|
||||||
|
int sock, short event, IOEventCallback callback, const int timeout)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
|
||||||
|
pTask->thread_data = pThread;
|
||||||
|
pTask->event.fd = sock;
|
||||||
|
pTask->event.callback = callback;
|
||||||
|
if (ioevent_attach(&pThread->ev_puller,
|
||||||
|
sock, event, pTask) < 0)
|
||||||
|
{
|
||||||
|
result = errno != 0 ? errno : ENOENT;
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"ioevent_attach fail, " \
|
||||||
|
"errno: %d, error info: %s", \
|
||||||
|
__LINE__, result, STRERROR(result));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
pTask->event.timer.data = pTask;
|
||||||
|
pTask->event.timer.expires = g_current_time + timeout;
|
||||||
|
result = fast_timer_add(&pThread->timer, &pTask->event.timer);
|
||||||
|
if (result != 0)
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"fast_timer_add fail, " \
|
||||||
|
"errno: %d, error info: %s", \
|
||||||
|
__LINE__, result, STRERROR(result));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
#ifndef _IOEVENT_LOOP_H
|
||||||
|
#define _IOEVENT_LOOP_H
|
||||||
|
|
||||||
|
#include "fast_task_queue.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int ioevent_loop(struct nio_thread_data *pThreadData,
|
||||||
|
IOEventCallback recv_notify_callback, TaskCleanUpCallBack
|
||||||
|
clean_up_callback, volatile bool *continue_flag);
|
||||||
|
|
||||||
|
int ioevent_set(struct fast_task_info *pTask, struct nio_thread_data *pThread,
|
||||||
|
int sock, short event, IOEventCallback callback, const int timeout);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
@ -0,0 +1,163 @@
|
||||||
|
/**
|
||||||
|
* This source file is used to print out a stack-trace when your program
|
||||||
|
* segfaults. It is relatively reliable and spot-on accurate.
|
||||||
|
*
|
||||||
|
* This code is in the public domain. Use it as you see fit, some credit
|
||||||
|
* would be appreciated, but is not a prerequisite for usage. Feedback
|
||||||
|
* on it's use would encourage further development and maintenance.
|
||||||
|
*
|
||||||
|
* Due to a bug in gcc-4.x.x you currently have to compile as C++ if you want
|
||||||
|
* demangling to work.
|
||||||
|
*
|
||||||
|
* Please note that it's been ported into my ULS library, thus the check for
|
||||||
|
*
|
||||||
|
* Author: Jaco Kroon <jaco@kroon.co.za>
|
||||||
|
*
|
||||||
|
* Copyright (C) 2005 - 2009 Jaco Kroon
|
||||||
|
*/
|
||||||
|
#ifndef _GNU_SOURCE
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Bug in gcc prevents from using CPP_DEMANGLE in pure "C" */
|
||||||
|
#if !defined(__cplusplus) && !defined(NO_CPP_DEMANGLE)
|
||||||
|
#define NO_CPP_DEMANGLE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <memory.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <ucontext.h>
|
||||||
|
#include <dlfcn.h>
|
||||||
|
#include "logger.h"
|
||||||
|
#include "shared_func.h"
|
||||||
|
#ifndef NO_CPP_DEMANGLE
|
||||||
|
#include <cxxabi.h>
|
||||||
|
#ifdef __cplusplus
|
||||||
|
using __cxxabiv1::__cxa_demangle;
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(REG_RIP)
|
||||||
|
# define SIGSEGV_STACK_IA64
|
||||||
|
# define REGFORMAT "%016lx"
|
||||||
|
#elif defined(REG_EIP)
|
||||||
|
# define SIGSEGV_STACK_X86
|
||||||
|
# define REGFORMAT "%08x"
|
||||||
|
#else
|
||||||
|
# define SIGSEGV_STACK_GENERIC
|
||||||
|
# define REGFORMAT "%x"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern char g_exe_name[256];
|
||||||
|
|
||||||
|
void signal_stack_trace_print(int signum, siginfo_t *info, void *ptr)
|
||||||
|
{
|
||||||
|
static const char *si_codes[3] = {"", "SEGV_MAPERR", "SEGV_ACCERR"};
|
||||||
|
|
||||||
|
int i, f = 0;
|
||||||
|
ucontext_t *ucontext;
|
||||||
|
Dl_info dlinfo;
|
||||||
|
void **bp = NULL;
|
||||||
|
void *ip = NULL;
|
||||||
|
char cmd[256];
|
||||||
|
char buff[256];
|
||||||
|
char output[8 * 1024];
|
||||||
|
char *pCurrent;
|
||||||
|
|
||||||
|
pCurrent = output;
|
||||||
|
ucontext = (ucontext_t*)ptr;
|
||||||
|
pCurrent += sprintf(pCurrent, "Segmentation Fault!\n");
|
||||||
|
pCurrent += sprintf(pCurrent, "\tinfo.si_signo = %d\n", signum);
|
||||||
|
pCurrent += sprintf(pCurrent, "\tinfo.si_errno = %d\n", info->si_errno);
|
||||||
|
pCurrent += sprintf(pCurrent, "\tinfo.si_code = %d (%s)\n", \
|
||||||
|
info->si_code, si_codes[info->si_code]);
|
||||||
|
pCurrent += sprintf(pCurrent, "\tinfo.si_addr = %p\n", info->si_addr);
|
||||||
|
for(i = 0; i < NGREG; i++)
|
||||||
|
{
|
||||||
|
pCurrent += sprintf(pCurrent, "\treg[%02d] = 0x"REGFORMAT"\n",
|
||||||
|
i, ucontext->uc_mcontext.gregs[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef SIGSEGV_NOSTACK
|
||||||
|
#if defined(SIGSEGV_STACK_IA64) || defined(SIGSEGV_STACK_X86)
|
||||||
|
#if defined(SIGSEGV_STACK_IA64)
|
||||||
|
ip = (void*)ucontext->uc_mcontext.gregs[REG_RIP];
|
||||||
|
bp = (void**)ucontext->uc_mcontext.gregs[REG_RBP];
|
||||||
|
#elif defined(SIGSEGV_STACK_X86)
|
||||||
|
ip = (void*)ucontext->uc_mcontext.gregs[REG_EIP];
|
||||||
|
bp = (void**)ucontext->uc_mcontext.gregs[REG_EBP];
|
||||||
|
#endif
|
||||||
|
|
||||||
|
pCurrent += sprintf(pCurrent, "\tStack trace:\n");
|
||||||
|
while(bp && ip)
|
||||||
|
{
|
||||||
|
const char *symname;
|
||||||
|
#ifndef NO_CPP_DEMANGLE
|
||||||
|
int status;
|
||||||
|
char * tmp;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if(!dladdr(ip, &dlinfo))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
symname = dlinfo.dli_sname;
|
||||||
|
|
||||||
|
#ifndef NO_CPP_DEMANGLE
|
||||||
|
tmp = __cxa_demangle(symname, NULL, 0, &status);
|
||||||
|
|
||||||
|
if (status == 0 && tmp)
|
||||||
|
{
|
||||||
|
symname = tmp;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
sprintf(cmd, "addr2line -e %s %p", g_exe_name, ip);
|
||||||
|
if (getExecResult(cmd, buff, sizeof(buff)) != 0)
|
||||||
|
{
|
||||||
|
*buff = '0';
|
||||||
|
}
|
||||||
|
|
||||||
|
pCurrent += sprintf(pCurrent, "\t\t% 2d: %p <%s+%lu> (%s in %s)\n",
|
||||||
|
++f, ip, symname,
|
||||||
|
(unsigned long)ip-(unsigned long)dlinfo.dli_saddr,
|
||||||
|
trim_right(buff), dlinfo.dli_fname);
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef NO_CPP_DEMANGLE
|
||||||
|
if (tmp)
|
||||||
|
{
|
||||||
|
free(tmp);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if(dlinfo.dli_sname && !strcmp(dlinfo.dli_sname, "main"))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ip = bp[1];
|
||||||
|
bp = (void**)bp[0];
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
pCurrent += sprintf(pCurrent, "\tStack trace (non-dedicated):\n");
|
||||||
|
sz = backtrace(bt, 20);
|
||||||
|
strings = backtrace_symbols(bt, sz);
|
||||||
|
for(i = 0; i < sz; ++i)
|
||||||
|
{
|
||||||
|
pCurrent += sprintf(pCurrent, "\t\t%s\n", strings[i]);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
pCurrent += sprintf(pCurrent, "\tEnd of stack trace.\n");
|
||||||
|
#else
|
||||||
|
pCurrent += sprintf(pCurrent, "\tNot printing stack strace.\n");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
log_it_ex(&g_log_context, LOG_CRIT, output, pCurrent - output);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2008 Happy Fish / YuQing
|
||||||
|
*
|
||||||
|
* FastDFS may be copied only under the terms of the GNU General
|
||||||
|
* Public License V3, which may be found in the FastDFS source kit.
|
||||||
|
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||||
|
**/
|
||||||
|
|
||||||
|
//linux_stack_trace.h
|
||||||
|
#ifndef LINUX_STACK_TRACE_H
|
||||||
|
#define LINUX_STACK_TRACE_H
|
||||||
|
|
||||||
|
#include "common_define.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void signal_stack_trace_print(int signum, siginfo_t *info, void *ptr);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
@ -0,0 +1,134 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2008 Happy Fish / YuQing
|
||||||
|
*
|
||||||
|
* FastDFS may be copied only under the terms of the GNU General
|
||||||
|
* Public License V3, which may be found in the FastDFS source kit.
|
||||||
|
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||||
|
**/
|
||||||
|
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include "logger.h"
|
||||||
|
#include "sockopt.h"
|
||||||
|
#include "shared_func.h"
|
||||||
|
#include "local_ip_func.h"
|
||||||
|
|
||||||
|
int g_local_host_ip_count = 0;
|
||||||
|
char g_local_host_ip_addrs[FAST_MAX_LOCAL_IP_ADDRS * \
|
||||||
|
IP_ADDRESS_SIZE];
|
||||||
|
char g_if_alias_prefix[FAST_IF_ALIAS_PREFIX_MAX_SIZE] = {0};
|
||||||
|
|
||||||
|
bool is_local_host_ip(const char *client_ip)
|
||||||
|
{
|
||||||
|
char *p;
|
||||||
|
char *pEnd;
|
||||||
|
|
||||||
|
pEnd = g_local_host_ip_addrs + \
|
||||||
|
IP_ADDRESS_SIZE * g_local_host_ip_count;
|
||||||
|
for (p=g_local_host_ip_addrs; p<pEnd; p+=IP_ADDRESS_SIZE)
|
||||||
|
{
|
||||||
|
if (strcmp(client_ip, p) == 0)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int insert_into_local_host_ip(const char *client_ip)
|
||||||
|
{
|
||||||
|
if (is_local_host_ip(client_ip))
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_local_host_ip_count >= FAST_MAX_LOCAL_IP_ADDRS)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
strcpy(g_local_host_ip_addrs + \
|
||||||
|
IP_ADDRESS_SIZE * g_local_host_ip_count, \
|
||||||
|
client_ip);
|
||||||
|
g_local_host_ip_count++;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void log_local_host_ip_addrs()
|
||||||
|
{
|
||||||
|
char *p;
|
||||||
|
char *pEnd;
|
||||||
|
char buff[512];
|
||||||
|
int len;
|
||||||
|
|
||||||
|
len = sprintf(buff, "local_host_ip_count: %d,", g_local_host_ip_count);
|
||||||
|
pEnd = g_local_host_ip_addrs + \
|
||||||
|
IP_ADDRESS_SIZE * g_local_host_ip_count;
|
||||||
|
for (p=g_local_host_ip_addrs; p<pEnd; p+=IP_ADDRESS_SIZE)
|
||||||
|
{
|
||||||
|
len += sprintf(buff + len, " %s", p);
|
||||||
|
}
|
||||||
|
|
||||||
|
logInfo("%s", buff);
|
||||||
|
}
|
||||||
|
|
||||||
|
void load_local_host_ip_addrs()
|
||||||
|
{
|
||||||
|
#define STORAGE_MAX_ALIAS_PREFIX_COUNT 4
|
||||||
|
char ip_addresses[FAST_MAX_LOCAL_IP_ADDRS][IP_ADDRESS_SIZE];
|
||||||
|
int count;
|
||||||
|
int k;
|
||||||
|
char *if_alias_prefixes[STORAGE_MAX_ALIAS_PREFIX_COUNT];
|
||||||
|
int alias_count;
|
||||||
|
|
||||||
|
insert_into_local_host_ip("127.0.0.1");
|
||||||
|
|
||||||
|
memset(if_alias_prefixes, 0, sizeof(if_alias_prefixes));
|
||||||
|
if (*g_if_alias_prefix == '\0')
|
||||||
|
{
|
||||||
|
alias_count = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
alias_count = splitEx(g_if_alias_prefix, ',', \
|
||||||
|
if_alias_prefixes, STORAGE_MAX_ALIAS_PREFIX_COUNT);
|
||||||
|
for (k=0; k<alias_count; k++)
|
||||||
|
{
|
||||||
|
trim(if_alias_prefixes[k]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gethostaddrs(if_alias_prefixes, alias_count, ip_addresses, \
|
||||||
|
FAST_MAX_LOCAL_IP_ADDRS, &count) != 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (k=0; k<count; k++)
|
||||||
|
{
|
||||||
|
insert_into_local_host_ip(ip_addresses[k]);
|
||||||
|
}
|
||||||
|
|
||||||
|
log_local_host_ip_addrs();
|
||||||
|
//print_local_host_ip_addrs();
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_local_host_ip_addrs()
|
||||||
|
{
|
||||||
|
char *p;
|
||||||
|
char *pEnd;
|
||||||
|
|
||||||
|
printf("local_host_ip_count=%d\n", g_local_host_ip_count);
|
||||||
|
pEnd = g_local_host_ip_addrs + \
|
||||||
|
IP_ADDRESS_SIZE * g_local_host_ip_count;
|
||||||
|
for (p=g_local_host_ip_addrs; p<pEnd; p+=IP_ADDRESS_SIZE)
|
||||||
|
{
|
||||||
|
printf("%d. %s\n", (int)((p-g_local_host_ip_addrs)/ \
|
||||||
|
IP_ADDRESS_SIZE)+1, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2008 Happy Fish / YuQing
|
||||||
|
*
|
||||||
|
* FastDFS may be copied only under the terms of the GNU General
|
||||||
|
* Public License V3, which may be found in the FastDFS source kit.
|
||||||
|
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||||
|
**/
|
||||||
|
|
||||||
|
//local_ip_func.h
|
||||||
|
|
||||||
|
#ifndef _LOCAL_IP_FUNC_H
|
||||||
|
#define _LOCAL_IP_FUNC_H
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include "common_define.h"
|
||||||
|
|
||||||
|
#define FAST_IF_ALIAS_PREFIX_MAX_SIZE 32
|
||||||
|
#define FAST_MAX_LOCAL_IP_ADDRS 16
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern int g_local_host_ip_count;
|
||||||
|
extern char g_local_host_ip_addrs[FAST_MAX_LOCAL_IP_ADDRS * \
|
||||||
|
IP_ADDRESS_SIZE];
|
||||||
|
extern char g_if_alias_prefix[FAST_IF_ALIAS_PREFIX_MAX_SIZE];
|
||||||
|
|
||||||
|
void load_local_host_ip_addrs();
|
||||||
|
bool is_local_host_ip(const char *client_ip);
|
||||||
|
int insert_into_local_host_ip(const char *client_ip);
|
||||||
|
void print_local_host_ip_addrs();
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
@ -0,0 +1,668 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2008 Happy Fish / YuQing
|
||||||
|
*
|
||||||
|
* FastDFS may be copied only under the terms of the GNU General
|
||||||
|
* Public License V3, which may be found in the FastDFS source kit.
|
||||||
|
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||||
|
**/
|
||||||
|
|
||||||
|
#include <limits.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/file.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include "shared_func.h"
|
||||||
|
#include "pthread_func.h"
|
||||||
|
#include "sched_thread.h"
|
||||||
|
#include "logger.h"
|
||||||
|
|
||||||
|
#ifndef LINE_MAX
|
||||||
|
#define LINE_MAX 2048
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define LOG_BUFF_SIZE 64 * 1024
|
||||||
|
|
||||||
|
LogContext g_log_context = {LOG_INFO, STDERR_FILENO, NULL};
|
||||||
|
|
||||||
|
static int log_fsync(LogContext *pContext, const bool bNeedLock);
|
||||||
|
|
||||||
|
static int check_and_mk_log_dir(const char *base_path)
|
||||||
|
{
|
||||||
|
char data_path[MAX_PATH_SIZE];
|
||||||
|
|
||||||
|
snprintf(data_path, sizeof(data_path), "%s/logs", base_path);
|
||||||
|
if (!fileExists(data_path))
|
||||||
|
{
|
||||||
|
if (mkdir(data_path, 0755) != 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "mkdir \"%s\" fail, " \
|
||||||
|
"errno: %d, error info: %s", \
|
||||||
|
data_path, errno, STRERROR(errno));
|
||||||
|
return errno != 0 ? errno : EPERM;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int log_init()
|
||||||
|
{
|
||||||
|
if (g_log_context.log_buff != NULL)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return log_init_ex(&g_log_context);
|
||||||
|
}
|
||||||
|
|
||||||
|
int log_init_ex(LogContext *pContext)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
|
||||||
|
memset(pContext, 0, sizeof(LogContext));
|
||||||
|
pContext->log_level = LOG_INFO;
|
||||||
|
pContext->log_fd = STDERR_FILENO;
|
||||||
|
pContext->log_to_cache = false;
|
||||||
|
pContext->rotate_immediately = false;
|
||||||
|
pContext->time_precision = LOG_TIME_PRECISION_SECOND;
|
||||||
|
|
||||||
|
pContext->log_buff = (char *)malloc(LOG_BUFF_SIZE);
|
||||||
|
if (pContext->log_buff == NULL)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "malloc %d bytes fail, " \
|
||||||
|
"errno: %d, error info: %s", \
|
||||||
|
LOG_BUFF_SIZE, errno, STRERROR(errno));
|
||||||
|
return errno != 0 ? errno : ENOMEM;
|
||||||
|
}
|
||||||
|
pContext->pcurrent_buff = pContext->log_buff;
|
||||||
|
|
||||||
|
if ((result=init_pthread_lock(&(pContext->log_thread_lock))) != 0)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int log_open(LogContext *pContext)
|
||||||
|
{
|
||||||
|
if ((pContext->log_fd = open(pContext->log_filename, O_WRONLY | \
|
||||||
|
O_CREAT | O_APPEND, 0644)) < 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "open log file \"%s\" to write fail, " \
|
||||||
|
"errno: %d, error info: %s", \
|
||||||
|
pContext->log_filename, errno, STRERROR(errno));
|
||||||
|
pContext->log_fd = STDERR_FILENO;
|
||||||
|
return errno != 0 ? errno : EACCES;
|
||||||
|
}
|
||||||
|
|
||||||
|
pContext->current_size = lseek(pContext->log_fd, 0, SEEK_END);
|
||||||
|
if (pContext->current_size < 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "lseek file \"%s\" fail, " \
|
||||||
|
"errno: %d, error info: %s", \
|
||||||
|
pContext->log_filename, errno, STRERROR(errno));
|
||||||
|
return errno != 0 ? errno : EACCES;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int log_set_prefix_ex(LogContext *pContext, const char *base_path, \
|
||||||
|
const char *filename_prefix)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
|
||||||
|
if ((result=check_and_mk_log_dir(base_path)) != 0)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(pContext->log_filename, MAX_PATH_SIZE, "%s/logs/%s.log", \
|
||||||
|
base_path, filename_prefix);
|
||||||
|
|
||||||
|
return log_open(pContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
int log_set_filename_ex(LogContext *pContext, const char *log_filename)
|
||||||
|
{
|
||||||
|
snprintf(pContext->log_filename, MAX_PATH_SIZE, "%s", log_filename);
|
||||||
|
return log_open(pContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
void log_set_cache_ex(LogContext *pContext, const bool bLogCache)
|
||||||
|
{
|
||||||
|
pContext->log_to_cache = bLogCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
void log_set_time_precision(LogContext *pContext, const int time_precision)
|
||||||
|
{
|
||||||
|
pContext->time_precision = time_precision;
|
||||||
|
}
|
||||||
|
|
||||||
|
void log_destroy_ex(LogContext *pContext)
|
||||||
|
{
|
||||||
|
if (pContext->log_fd >= 0 && pContext->log_fd != STDERR_FILENO)
|
||||||
|
{
|
||||||
|
log_fsync(pContext, true);
|
||||||
|
|
||||||
|
close(pContext->log_fd);
|
||||||
|
pContext->log_fd = STDERR_FILENO;
|
||||||
|
|
||||||
|
pthread_mutex_destroy(&pContext->log_thread_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pContext->log_buff != NULL)
|
||||||
|
{
|
||||||
|
free(pContext->log_buff);
|
||||||
|
pContext->log_buff = NULL;
|
||||||
|
pContext->pcurrent_buff = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int log_sync_func(void *args)
|
||||||
|
{
|
||||||
|
if (args == NULL)
|
||||||
|
{
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return log_fsync((LogContext *)args, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
int log_notify_rotate(void *args)
|
||||||
|
{
|
||||||
|
if (args == NULL)
|
||||||
|
{
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
((LogContext *)args)->rotate_immediately = true;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int log_rotate(LogContext *pContext)
|
||||||
|
{
|
||||||
|
struct tm tm;
|
||||||
|
time_t current_time;
|
||||||
|
char new_filename[MAX_PATH_SIZE + 32];
|
||||||
|
|
||||||
|
if (*(pContext->log_filename) == '\0')
|
||||||
|
{
|
||||||
|
return ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
close(pContext->log_fd);
|
||||||
|
|
||||||
|
current_time = get_current_time();
|
||||||
|
localtime_r(¤t_time, &tm);
|
||||||
|
sprintf(new_filename, "%s.%04d%02d%02d_%02d%02d%02d", \
|
||||||
|
pContext->log_filename, \
|
||||||
|
tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday, \
|
||||||
|
tm.tm_hour, tm.tm_min, tm.tm_sec);
|
||||||
|
if (rename(pContext->log_filename, new_filename) != 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "file: "__FILE__", line: %d, " \
|
||||||
|
"rename %s to %s fail, errno: %d, error info: %s", \
|
||||||
|
__LINE__, pContext->log_filename, new_filename, \
|
||||||
|
errno, STRERROR(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
return log_open(pContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int log_check_rotate(LogContext *pContext, const bool bNeedLock)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
|
||||||
|
if (pContext->log_fd == STDERR_FILENO)
|
||||||
|
{
|
||||||
|
if (pContext->current_size > 0)
|
||||||
|
{
|
||||||
|
pContext->current_size = 0;
|
||||||
|
}
|
||||||
|
return ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bNeedLock)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&(pContext->log_thread_lock));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pContext->rotate_immediately)
|
||||||
|
{
|
||||||
|
result = log_rotate(pContext);
|
||||||
|
pContext->rotate_immediately = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bNeedLock)
|
||||||
|
{
|
||||||
|
pthread_mutex_unlock(&(pContext->log_thread_lock));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int log_fsync(LogContext *pContext, const bool bNeedLock)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
int lock_res;
|
||||||
|
int write_bytes;
|
||||||
|
|
||||||
|
write_bytes = pContext->pcurrent_buff - pContext->log_buff;
|
||||||
|
if (write_bytes == 0)
|
||||||
|
{
|
||||||
|
if (!pContext->rotate_immediately)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return log_check_rotate(pContext, bNeedLock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bNeedLock && ((lock_res=pthread_mutex_lock( \
|
||||||
|
&(pContext->log_thread_lock))) != 0))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "file: "__FILE__", line: %d, " \
|
||||||
|
"call pthread_mutex_lock fail, " \
|
||||||
|
"errno: %d, error info: %s", \
|
||||||
|
__LINE__, lock_res, STRERROR(lock_res));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pContext->rotate_size > 0)
|
||||||
|
{
|
||||||
|
pContext->current_size += write_bytes;
|
||||||
|
if (pContext->current_size > pContext->rotate_size)
|
||||||
|
{
|
||||||
|
pContext->rotate_immediately = true;
|
||||||
|
log_check_rotate(pContext, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result = 0;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
write_bytes = pContext->pcurrent_buff - pContext->log_buff;
|
||||||
|
if (write(pContext->log_fd, pContext->log_buff, write_bytes) != \
|
||||||
|
write_bytes)
|
||||||
|
{
|
||||||
|
result = errno != 0 ? errno : EIO;
|
||||||
|
fprintf(stderr, "file: "__FILE__", line: %d, " \
|
||||||
|
"call write fail, errno: %d, error info: %s\n",\
|
||||||
|
__LINE__, result, STRERROR(result));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pContext->log_fd != STDERR_FILENO)
|
||||||
|
{
|
||||||
|
if (fsync(pContext->log_fd) != 0)
|
||||||
|
{
|
||||||
|
result = errno != 0 ? errno : EIO;
|
||||||
|
fprintf(stderr, "file: "__FILE__", line: %d, " \
|
||||||
|
"call fsync fail, errno: %d, error info: %s\n",\
|
||||||
|
__LINE__, result, STRERROR(result));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pContext->rotate_immediately)
|
||||||
|
{
|
||||||
|
result = log_check_rotate(pContext, false);
|
||||||
|
}
|
||||||
|
} while (0);
|
||||||
|
|
||||||
|
pContext->pcurrent_buff = pContext->log_buff;
|
||||||
|
if (bNeedLock && ((lock_res=pthread_mutex_unlock( \
|
||||||
|
&(pContext->log_thread_lock))) != 0))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "file: "__FILE__", line: %d, " \
|
||||||
|
"call pthread_mutex_unlock fail, " \
|
||||||
|
"errno: %d, error info: %s", \
|
||||||
|
__LINE__, lock_res, STRERROR(lock_res));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void doLogEx(LogContext *pContext, struct timeval *tv, \
|
||||||
|
const char *caption, const char *text, const int text_len, \
|
||||||
|
const bool bNeedSync)
|
||||||
|
{
|
||||||
|
struct tm tm;
|
||||||
|
int time_fragment;
|
||||||
|
int buff_len;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
if (pContext->time_precision == LOG_TIME_PRECISION_SECOND)
|
||||||
|
{
|
||||||
|
time_fragment = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (pContext->time_precision == LOG_TIME_PRECISION_MSECOND)
|
||||||
|
{
|
||||||
|
time_fragment = tv->tv_usec / 1000;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
time_fragment = tv->tv_usec;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
localtime_r(&tv->tv_sec, &tm);
|
||||||
|
if ((result=pthread_mutex_lock(&pContext->log_thread_lock)) != 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "file: "__FILE__", line: %d, " \
|
||||||
|
"call pthread_mutex_lock fail, " \
|
||||||
|
"errno: %d, error info: %s", \
|
||||||
|
__LINE__, result, STRERROR(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (text_len + 64 > LOG_BUFF_SIZE)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "file: "__FILE__", line: %d, " \
|
||||||
|
"log buff size: %d < log text length: %d ", \
|
||||||
|
__LINE__, LOG_BUFF_SIZE, text_len + 64);
|
||||||
|
pthread_mutex_unlock(&(pContext->log_thread_lock));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((pContext->pcurrent_buff - pContext->log_buff) + text_len + 64 \
|
||||||
|
> LOG_BUFF_SIZE)
|
||||||
|
{
|
||||||
|
log_fsync(pContext, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pContext->time_precision == LOG_TIME_PRECISION_SECOND)
|
||||||
|
{
|
||||||
|
buff_len = sprintf(pContext->pcurrent_buff, \
|
||||||
|
"[%04d-%02d-%02d %02d:%02d:%02d] ", \
|
||||||
|
tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday, \
|
||||||
|
tm.tm_hour, tm.tm_min, tm.tm_sec);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
buff_len = sprintf(pContext->pcurrent_buff, \
|
||||||
|
"[%04d-%02d-%02d %02d:%02d:%02d.%03d] ", \
|
||||||
|
tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday, \
|
||||||
|
tm.tm_hour, tm.tm_min, tm.tm_sec, time_fragment);
|
||||||
|
}
|
||||||
|
pContext->pcurrent_buff += buff_len;
|
||||||
|
|
||||||
|
if (caption != NULL)
|
||||||
|
{
|
||||||
|
buff_len = sprintf(pContext->pcurrent_buff, "%s - ", caption);
|
||||||
|
pContext->pcurrent_buff += buff_len;
|
||||||
|
}
|
||||||
|
memcpy(pContext->pcurrent_buff, text, text_len);
|
||||||
|
pContext->pcurrent_buff += text_len;
|
||||||
|
*pContext->pcurrent_buff++ = '\n';
|
||||||
|
|
||||||
|
if (!pContext->log_to_cache || bNeedSync)
|
||||||
|
{
|
||||||
|
log_fsync(pContext, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((result=pthread_mutex_unlock(&(pContext->log_thread_lock))) != 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "file: "__FILE__", line: %d, " \
|
||||||
|
"call pthread_mutex_unlock fail, " \
|
||||||
|
"errno: %d, error info: %s", \
|
||||||
|
__LINE__, result, STRERROR(result));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void doLog(LogContext *pContext, const char *caption, \
|
||||||
|
const char *text, const int text_len, const bool bNeedSync)
|
||||||
|
{
|
||||||
|
struct timeval tv;
|
||||||
|
|
||||||
|
if (pContext->time_precision == LOG_TIME_PRECISION_SECOND)
|
||||||
|
{
|
||||||
|
tv.tv_sec = get_current_time();
|
||||||
|
tv.tv_usec = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gettimeofday(&tv, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
doLogEx(pContext, &tv, caption, text, text_len, bNeedSync);
|
||||||
|
}
|
||||||
|
|
||||||
|
void log_it_ex1(LogContext *pContext, const int priority, \
|
||||||
|
const char *text, const int text_len)
|
||||||
|
{
|
||||||
|
bool bNeedSync;
|
||||||
|
char *caption;
|
||||||
|
|
||||||
|
switch(priority)
|
||||||
|
{
|
||||||
|
case LOG_DEBUG:
|
||||||
|
bNeedSync = true;
|
||||||
|
caption = "DEBUG";
|
||||||
|
break;
|
||||||
|
case LOG_INFO:
|
||||||
|
bNeedSync = true;
|
||||||
|
caption = "INFO";
|
||||||
|
break;
|
||||||
|
case LOG_NOTICE:
|
||||||
|
bNeedSync = false;
|
||||||
|
caption = "NOTICE";
|
||||||
|
break;
|
||||||
|
case LOG_WARNING:
|
||||||
|
bNeedSync = false;
|
||||||
|
caption = "WARNING";
|
||||||
|
break;
|
||||||
|
case LOG_ERR:
|
||||||
|
bNeedSync = false;
|
||||||
|
caption = "ERROR";
|
||||||
|
break;
|
||||||
|
case LOG_CRIT:
|
||||||
|
bNeedSync = true;
|
||||||
|
caption = "CRIT";
|
||||||
|
break;
|
||||||
|
case LOG_ALERT:
|
||||||
|
bNeedSync = true;
|
||||||
|
caption = "ALERT";
|
||||||
|
break;
|
||||||
|
case LOG_EMERG:
|
||||||
|
bNeedSync = true;
|
||||||
|
caption = "EMERG";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
bNeedSync = false;
|
||||||
|
caption = "UNKOWN";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
doLog(pContext, caption, text, text_len, bNeedSync);
|
||||||
|
}
|
||||||
|
|
||||||
|
void log_it_ex(LogContext *pContext, const int priority, const char *format, ...)
|
||||||
|
{
|
||||||
|
bool bNeedSync;
|
||||||
|
char text[LINE_MAX];
|
||||||
|
char *caption;
|
||||||
|
int len;
|
||||||
|
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, format);
|
||||||
|
len = vsnprintf(text, sizeof(text), format, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
switch(priority)
|
||||||
|
{
|
||||||
|
case LOG_DEBUG:
|
||||||
|
bNeedSync = true;
|
||||||
|
caption = "DEBUG";
|
||||||
|
break;
|
||||||
|
case LOG_INFO:
|
||||||
|
bNeedSync = true;
|
||||||
|
caption = "INFO";
|
||||||
|
break;
|
||||||
|
case LOG_NOTICE:
|
||||||
|
bNeedSync = false;
|
||||||
|
caption = "NOTICE";
|
||||||
|
break;
|
||||||
|
case LOG_WARNING:
|
||||||
|
bNeedSync = false;
|
||||||
|
caption = "WARNING";
|
||||||
|
break;
|
||||||
|
case LOG_ERR:
|
||||||
|
bNeedSync = false;
|
||||||
|
caption = "ERROR";
|
||||||
|
break;
|
||||||
|
case LOG_CRIT:
|
||||||
|
bNeedSync = true;
|
||||||
|
caption = "CRIT";
|
||||||
|
break;
|
||||||
|
case LOG_ALERT:
|
||||||
|
bNeedSync = true;
|
||||||
|
caption = "ALERT";
|
||||||
|
break;
|
||||||
|
case LOG_EMERG:
|
||||||
|
bNeedSync = true;
|
||||||
|
caption = "EMERG";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
bNeedSync = false;
|
||||||
|
caption = "UNKOWN";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
doLog(pContext, caption, text, len, bNeedSync);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#define _DO_LOG(pContext, priority, caption, bNeedSync) \
|
||||||
|
char text[LINE_MAX]; \
|
||||||
|
int len; \
|
||||||
|
\
|
||||||
|
if (pContext->log_level < priority) \
|
||||||
|
{ \
|
||||||
|
return; \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
{ \
|
||||||
|
va_list ap; \
|
||||||
|
va_start(ap, format); \
|
||||||
|
len = vsnprintf(text, sizeof(text), format, ap); \
|
||||||
|
va_end(ap); \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
doLog(pContext, caption, text, len, bNeedSync); \
|
||||||
|
|
||||||
|
|
||||||
|
void logEmergEx(LogContext *pContext, const char *format, ...)
|
||||||
|
{
|
||||||
|
_DO_LOG(pContext, LOG_EMERG, "EMERG", true)
|
||||||
|
}
|
||||||
|
|
||||||
|
void logAlertEx(LogContext *pContext, const char *format, ...)
|
||||||
|
{
|
||||||
|
_DO_LOG(pContext, LOG_ALERT, "ALERT", true)
|
||||||
|
}
|
||||||
|
|
||||||
|
void logCritEx(LogContext *pContext, const char *format, ...)
|
||||||
|
{
|
||||||
|
_DO_LOG(pContext, LOG_CRIT, "CRIT", true)
|
||||||
|
}
|
||||||
|
|
||||||
|
void logErrorEx(LogContext *pContext, const char *format, ...)
|
||||||
|
{
|
||||||
|
_DO_LOG(pContext, LOG_ERR, "ERROR", false)
|
||||||
|
}
|
||||||
|
|
||||||
|
void logWarningEx(LogContext *pContext, const char *format, ...)
|
||||||
|
{
|
||||||
|
_DO_LOG(pContext, LOG_WARNING, "WARNING", false)
|
||||||
|
}
|
||||||
|
|
||||||
|
void logNoticeEx(LogContext *pContext, const char *format, ...)
|
||||||
|
{
|
||||||
|
_DO_LOG(pContext, LOG_NOTICE, "NOTICE", false)
|
||||||
|
}
|
||||||
|
|
||||||
|
void logInfoEx(LogContext *pContext, const char *format, ...)
|
||||||
|
{
|
||||||
|
_DO_LOG(pContext, LOG_INFO, "INFO", false)
|
||||||
|
}
|
||||||
|
|
||||||
|
void logDebugEx(LogContext *pContext, const char *format, ...)
|
||||||
|
{
|
||||||
|
_DO_LOG(pContext, LOG_DEBUG, "DEBUG", false)
|
||||||
|
}
|
||||||
|
|
||||||
|
void logAccess(LogContext *pContext, struct timeval *tvStart, \
|
||||||
|
const char *format, ...)
|
||||||
|
{
|
||||||
|
char text[LINE_MAX];
|
||||||
|
int len;
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
va_start(ap, format);
|
||||||
|
len = vsnprintf(text, sizeof(text), format, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
doLogEx(pContext, tvStart, NULL, text, len, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef LOG_FORMAT_CHECK
|
||||||
|
|
||||||
|
void logEmerg(const char *format, ...)
|
||||||
|
{
|
||||||
|
_DO_LOG((&g_log_context), LOG_EMERG, "EMERG", true)
|
||||||
|
}
|
||||||
|
|
||||||
|
void logAlert(const char *format, ...)
|
||||||
|
{
|
||||||
|
_DO_LOG((&g_log_context), LOG_ALERT, "ALERT", true)
|
||||||
|
}
|
||||||
|
|
||||||
|
void logCrit(const char *format, ...)
|
||||||
|
{
|
||||||
|
_DO_LOG((&g_log_context), LOG_CRIT, "CRIT", true)
|
||||||
|
}
|
||||||
|
|
||||||
|
void logError(const char *format, ...)
|
||||||
|
{
|
||||||
|
_DO_LOG((&g_log_context), LOG_ERR, "ERROR", false)
|
||||||
|
}
|
||||||
|
|
||||||
|
void logWarning(const char *format, ...)
|
||||||
|
{
|
||||||
|
_DO_LOG((&g_log_context), LOG_WARNING, "WARNING", false)
|
||||||
|
}
|
||||||
|
|
||||||
|
void logNotice(const char *format, ...)
|
||||||
|
{
|
||||||
|
_DO_LOG((&g_log_context), LOG_NOTICE, "NOTICE", false)
|
||||||
|
}
|
||||||
|
|
||||||
|
void logInfo(const char *format, ...)
|
||||||
|
{
|
||||||
|
_DO_LOG((&g_log_context), LOG_INFO, "INFO", true)
|
||||||
|
}
|
||||||
|
|
||||||
|
void logDebug(const char *format, ...)
|
||||||
|
{
|
||||||
|
_DO_LOG((&g_log_context), LOG_DEBUG, "DEBUG", true)
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
@ -0,0 +1,212 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2008 Happy Fish / YuQing
|
||||||
|
*
|
||||||
|
* FastDFS may be copied only under the terms of the GNU General
|
||||||
|
* Public License V3, which may be found in the FastDFS source kit.
|
||||||
|
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||||
|
**/
|
||||||
|
|
||||||
|
//logger.h
|
||||||
|
#ifndef LOGGER_H
|
||||||
|
#define LOGGER_H
|
||||||
|
|
||||||
|
#include <syslog.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include "common_define.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define LOG_TIME_PRECISION_SECOND 's' //second
|
||||||
|
#define LOG_TIME_PRECISION_MSECOND 'm' //millisecond
|
||||||
|
#define LOG_TIME_PRECISION_USSECOND 'u' //microsecond
|
||||||
|
|
||||||
|
typedef struct log_context
|
||||||
|
{
|
||||||
|
/* log level value please see: sys/syslog.h
|
||||||
|
default value is LOG_INFO */
|
||||||
|
int log_level;
|
||||||
|
|
||||||
|
/* default value is STDERR_FILENO */
|
||||||
|
int log_fd;
|
||||||
|
|
||||||
|
/* cache buffer */
|
||||||
|
char *log_buff;
|
||||||
|
|
||||||
|
/* string end in the cache buffer for next sprintf */
|
||||||
|
char *pcurrent_buff;
|
||||||
|
|
||||||
|
/* mutext lock */
|
||||||
|
pthread_mutex_t log_thread_lock;
|
||||||
|
|
||||||
|
/*
|
||||||
|
rotate the log when the log file exceeds this parameter
|
||||||
|
rotate_size > 0 means need rotate log by log file size
|
||||||
|
*/
|
||||||
|
int64_t rotate_size;
|
||||||
|
|
||||||
|
/* log file current size */
|
||||||
|
int64_t current_size;
|
||||||
|
|
||||||
|
/* if write to buffer firstly, then sync to disk.
|
||||||
|
default value is false (no cache) */
|
||||||
|
bool log_to_cache;
|
||||||
|
|
||||||
|
/* if rotate the access log */
|
||||||
|
bool rotate_immediately;
|
||||||
|
|
||||||
|
/* time precision */
|
||||||
|
char time_precision;
|
||||||
|
|
||||||
|
/* save the log filename */
|
||||||
|
char log_filename[MAX_PATH_SIZE];
|
||||||
|
} LogContext;
|
||||||
|
|
||||||
|
extern LogContext g_log_context;
|
||||||
|
|
||||||
|
/** init function using global log context
|
||||||
|
* return: 0 for success, != 0 fail
|
||||||
|
*/
|
||||||
|
int log_init();
|
||||||
|
|
||||||
|
#define log_set_prefix(base_path, filename_prefix) \
|
||||||
|
log_set_prefix_ex(&g_log_context, base_path, filename_prefix)
|
||||||
|
|
||||||
|
#define log_set_filename(log_filename) \
|
||||||
|
log_set_filename_ex(&g_log_context, log_filename)
|
||||||
|
|
||||||
|
#define log_set_cache(bLogCache) log_set_cache_ex(&g_log_context, bLogCache)
|
||||||
|
|
||||||
|
#define log_destroy() log_destroy_ex(&g_log_context)
|
||||||
|
|
||||||
|
/** init function, use stderr for output by default
|
||||||
|
* parameters:
|
||||||
|
* pContext: the log context
|
||||||
|
* return: 0 for success, != 0 fail
|
||||||
|
*/
|
||||||
|
int log_init_ex(LogContext *pContext);
|
||||||
|
|
||||||
|
/** set log filename prefix, such as "tracker", the log filename will be
|
||||||
|
* ${base_path}/logs/tracker.log
|
||||||
|
* parameters:
|
||||||
|
* pContext: the log context
|
||||||
|
* base_path: base path
|
||||||
|
* filename_prefix: log filename prefix
|
||||||
|
* return: 0 for success, != 0 fail
|
||||||
|
*/
|
||||||
|
int log_set_prefix_ex(LogContext *pContext, const char *base_path, \
|
||||||
|
const char *filename_prefix);
|
||||||
|
|
||||||
|
/** set log filename
|
||||||
|
* parameters:
|
||||||
|
* pContext: the log context
|
||||||
|
* log_filename: log filename
|
||||||
|
* return: 0 for success, != 0 fail
|
||||||
|
*/
|
||||||
|
int log_set_filename_ex(LogContext *pContext, const char *log_filename);
|
||||||
|
|
||||||
|
/** set if use log cache
|
||||||
|
* parameters:
|
||||||
|
* pContext: the log context
|
||||||
|
* bLogCache: true for cache in buffer, false directly write to disk
|
||||||
|
* return: none
|
||||||
|
*/
|
||||||
|
void log_set_cache_ex(LogContext *pContext, const bool bLogCache);
|
||||||
|
|
||||||
|
/** set time precision
|
||||||
|
* parameters:
|
||||||
|
* pContext: the log context
|
||||||
|
* time_precision: the time precision
|
||||||
|
* return: none
|
||||||
|
*/
|
||||||
|
void log_set_time_precision(LogContext *pContext, const int time_precision);
|
||||||
|
|
||||||
|
/** destroy function
|
||||||
|
* parameters:
|
||||||
|
* pContext: the log context
|
||||||
|
* bLogCache: true for cache in buffer, false directly write to disk
|
||||||
|
* return: none
|
||||||
|
*/
|
||||||
|
void log_destroy_ex(LogContext *pContext);
|
||||||
|
|
||||||
|
/** log to file
|
||||||
|
* parameters:
|
||||||
|
* pContext: the log context
|
||||||
|
* priority: unix priority
|
||||||
|
* format: printf format
|
||||||
|
* ...: arguments for printf format
|
||||||
|
* return: none
|
||||||
|
*/
|
||||||
|
void log_it_ex(LogContext *pContext, const int priority, \
|
||||||
|
const char *format, ...);
|
||||||
|
|
||||||
|
/** log to file
|
||||||
|
* parameters:
|
||||||
|
* pContext: the log context
|
||||||
|
* priority: unix priority
|
||||||
|
* text: text string to log
|
||||||
|
* text_len: text string length (bytes)
|
||||||
|
* return: none
|
||||||
|
*/
|
||||||
|
void log_it_ex1(LogContext *pContext, const int priority, \
|
||||||
|
const char *text, const int text_len);
|
||||||
|
|
||||||
|
/** sync log buffer to log file
|
||||||
|
* parameters:
|
||||||
|
* args: should be (LogContext *)
|
||||||
|
* return: error no, 0 for success, != 0 fail
|
||||||
|
*/
|
||||||
|
int log_sync_func(void *args);
|
||||||
|
|
||||||
|
/** set rotate flag to true
|
||||||
|
* parameters:
|
||||||
|
* args: should be (LogContext *)
|
||||||
|
* return: error no, 0 for success, != 0 fail
|
||||||
|
*/
|
||||||
|
int log_notify_rotate(void *args);
|
||||||
|
|
||||||
|
void logEmergEx(LogContext *pContext, const char *format, ...);
|
||||||
|
void logCritEx(LogContext *pContext, const char *format, ...);
|
||||||
|
void logAlertEx(LogContext *pContext, const char *format, ...);
|
||||||
|
void logErrorEx(LogContext *pContext, const char *format, ...);
|
||||||
|
void logWarningEx(LogContext *pContext, const char *format, ...);
|
||||||
|
void logNoticeEx(LogContext *pContext, const char *format, ...);
|
||||||
|
void logInfoEx(LogContext *pContext, const char *format, ...);
|
||||||
|
void logDebugEx(LogContext *pContext, const char *format, ...);
|
||||||
|
void logAccess(LogContext *pContext, struct timeval *tvStart, \
|
||||||
|
const char *format, ...);
|
||||||
|
|
||||||
|
//#define LOG_FORMAT_CHECK
|
||||||
|
|
||||||
|
#ifdef LOG_FORMAT_CHECK /*only for format check*/
|
||||||
|
|
||||||
|
#define logEmerg printf
|
||||||
|
#define logCrit printf
|
||||||
|
#define logAlert printf
|
||||||
|
#define logError printf
|
||||||
|
#define logWarning printf
|
||||||
|
#define logNotice printf
|
||||||
|
#define logInfo printf
|
||||||
|
#define logDebug printf
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
/* following functions use global log context: g_log_context */
|
||||||
|
void logEmerg(const char *format, ...);
|
||||||
|
void logCrit(const char *format, ...);
|
||||||
|
void logAlert(const char *format, ...);
|
||||||
|
void logError(const char *format, ...);
|
||||||
|
void logWarning(const char *format, ...);
|
||||||
|
void logNotice(const char *format, ...);
|
||||||
|
void logInfo(const char *format, ...);
|
||||||
|
void logDebug(const char *format, ...);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
@ -0,0 +1,356 @@
|
||||||
|
#include "md5.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#define S11 7
|
||||||
|
#define S12 12
|
||||||
|
#define S13 17
|
||||||
|
#define S14 22
|
||||||
|
#define S21 5
|
||||||
|
#define S22 9
|
||||||
|
#define S23 14
|
||||||
|
#define S24 20
|
||||||
|
#define S31 4
|
||||||
|
#define S32 11
|
||||||
|
#define S33 16
|
||||||
|
#define S34 23
|
||||||
|
#define S41 6
|
||||||
|
#define S42 10
|
||||||
|
#define S43 15
|
||||||
|
#define S44 21
|
||||||
|
|
||||||
|
static void MD5Transform (UINT4[4], unsigned char[64]);
|
||||||
|
static void Encode (unsigned char *, UINT4 *, unsigned int);
|
||||||
|
static void Decode (UINT4 *, unsigned char *, unsigned int);
|
||||||
|
static void MD5_memcpy (POINTER, POINTER, unsigned int);
|
||||||
|
static void MD5_memset (POINTER, int, unsigned int);
|
||||||
|
|
||||||
|
static unsigned char PADDING[64] = {
|
||||||
|
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* F, G, H and I are basic MD5 functions.
|
||||||
|
*/
|
||||||
|
#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
|
||||||
|
#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
|
||||||
|
#define H(x, y, z) ((x) ^ (y) ^ (z))
|
||||||
|
#define I(x, y, z) ((y) ^ ((x) | (~z)))
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ROTATE_LEFT rotates x left n bits.
|
||||||
|
*/
|
||||||
|
#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
|
||||||
|
|
||||||
|
/*
|
||||||
|
* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. Rotation is
|
||||||
|
* separate from addition to prevent recomputation.
|
||||||
|
*/
|
||||||
|
#define FF(a, b, c, d, x, s, ac) { \
|
||||||
|
(a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \
|
||||||
|
(a) = ROTATE_LEFT ((a), (s)); \
|
||||||
|
(a) += (b); \
|
||||||
|
}
|
||||||
|
#define GG(a, b, c, d, x, s, ac) { \
|
||||||
|
(a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \
|
||||||
|
(a) = ROTATE_LEFT ((a), (s)); \
|
||||||
|
(a) += (b); \
|
||||||
|
}
|
||||||
|
#define HH(a, b, c, d, x, s, ac) { \
|
||||||
|
(a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \
|
||||||
|
(a) = ROTATE_LEFT ((a), (s)); \
|
||||||
|
(a) += (b); \
|
||||||
|
}
|
||||||
|
#define II(a, b, c, d, x, s, ac) { \
|
||||||
|
(a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \
|
||||||
|
(a) = ROTATE_LEFT ((a), (s)); \
|
||||||
|
(a) += (b); \
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MD5 initialization. Begins an MD5 operation, writing a new context.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
my_md5_init(MD5_CTX *context)
|
||||||
|
{
|
||||||
|
context->count[0] = context->count[1] = 0;
|
||||||
|
/*
|
||||||
|
* Load magic initialization constants.
|
||||||
|
*/
|
||||||
|
context->state[0] = 0x67452301;
|
||||||
|
context->state[1] = 0xefcdab89;
|
||||||
|
context->state[2] = 0x98badcfe;
|
||||||
|
context->state[3] = 0x10325476;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MD5 block update operation. Continues an MD5 message-digest operation,
|
||||||
|
* processing another message block, and updating the context.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
my_md5_update(MD5_CTX *context, unsigned char *input, unsigned int inputLen)
|
||||||
|
{
|
||||||
|
unsigned int i, index, partLen;
|
||||||
|
|
||||||
|
/* Compute number of bytes mod 64 */
|
||||||
|
index = (unsigned int) ((context->count[0] >> 3) & 0x3F);
|
||||||
|
|
||||||
|
/* Update number of bits */
|
||||||
|
if ((context->count[0] += ((UINT4) inputLen << 3)) < ((UINT4) inputLen << 3))
|
||||||
|
context->count[1]++;
|
||||||
|
context->count[1] += ((UINT4) inputLen >> 29);
|
||||||
|
|
||||||
|
partLen = 64 - index;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Transform as many times as possible.
|
||||||
|
*/
|
||||||
|
if (inputLen >= partLen) {
|
||||||
|
MD5_memcpy
|
||||||
|
((POINTER) & context->buffer[index], (POINTER) input, partLen);
|
||||||
|
MD5Transform(context->state, context->buffer);
|
||||||
|
|
||||||
|
for (i = partLen; i + 63 < inputLen; i += 64)
|
||||||
|
MD5Transform(context->state, &input[i]);
|
||||||
|
|
||||||
|
index = 0;
|
||||||
|
} else
|
||||||
|
i = 0;
|
||||||
|
|
||||||
|
/* Buffer remaining input */
|
||||||
|
MD5_memcpy
|
||||||
|
((POINTER) & context->buffer[index], (POINTER) & input[i],
|
||||||
|
inputLen - i);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MD5 finalization. Ends an MD5 message-digest operation, writing the the
|
||||||
|
* message digest and zeroizing the context.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
my_md5_final(unsigned char digest[16], MD5_CTX *context)
|
||||||
|
{
|
||||||
|
unsigned char bits[8];
|
||||||
|
unsigned int index, padLen;
|
||||||
|
|
||||||
|
/* Save number of bits */
|
||||||
|
Encode(bits, context->count, 8);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Pad out to 56 mod 64.
|
||||||
|
*/
|
||||||
|
index = (unsigned int) ((context->count[0] >> 3) & 0x3f);
|
||||||
|
padLen = (index < 56) ? (56 - index) : (120 - index);
|
||||||
|
my_md5_update(context, PADDING, padLen);
|
||||||
|
|
||||||
|
/* Append length (before padding) */
|
||||||
|
my_md5_update(context, bits, 8);
|
||||||
|
/* Store state in digest */
|
||||||
|
Encode(digest, context->state, 16);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Zeroize sensitive information.
|
||||||
|
*/
|
||||||
|
MD5_memset((POINTER) context, 0, sizeof(*context));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MD5 basic transformation. Transforms state based on block.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
MD5Transform(UINT4 state[4], unsigned char block[64])
|
||||||
|
{
|
||||||
|
UINT4 a = state[0], b = state[1], c = state[2], d = state[3],
|
||||||
|
x[16];
|
||||||
|
|
||||||
|
Decode(x, block, 64);
|
||||||
|
|
||||||
|
/* Round 1 */
|
||||||
|
FF(a, b, c, d, x[0], S11, 0xd76aa478); /* 1 */
|
||||||
|
FF(d, a, b, c, x[1], S12, 0xe8c7b756); /* 2 */
|
||||||
|
FF(c, d, a, b, x[2], S13, 0x242070db); /* 3 */
|
||||||
|
FF(b, c, d, a, x[3], S14, 0xc1bdceee); /* 4 */
|
||||||
|
FF(a, b, c, d, x[4], S11, 0xf57c0faf); /* 5 */
|
||||||
|
FF(d, a, b, c, x[5], S12, 0x4787c62a); /* 6 */
|
||||||
|
FF(c, d, a, b, x[6], S13, 0xa8304613); /* 7 */
|
||||||
|
FF(b, c, d, a, x[7], S14, 0xfd469501); /* 8 */
|
||||||
|
FF(a, b, c, d, x[8], S11, 0x698098d8); /* 9 */
|
||||||
|
FF(d, a, b, c, x[9], S12, 0x8b44f7af); /* 10 */
|
||||||
|
FF(c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
|
||||||
|
FF(b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
|
||||||
|
FF(a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
|
||||||
|
FF(d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
|
||||||
|
FF(c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
|
||||||
|
FF(b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
|
||||||
|
|
||||||
|
/* Round 2 */
|
||||||
|
GG(a, b, c, d, x[1], S21, 0xf61e2562); /* 17 */
|
||||||
|
GG(d, a, b, c, x[6], S22, 0xc040b340); /* 18 */
|
||||||
|
GG(c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
|
||||||
|
GG(b, c, d, a, x[0], S24, 0xe9b6c7aa); /* 20 */
|
||||||
|
GG(a, b, c, d, x[5], S21, 0xd62f105d); /* 21 */
|
||||||
|
GG(d, a, b, c, x[10], S22, 0x2441453); /* 22 */
|
||||||
|
GG(c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
|
||||||
|
GG(b, c, d, a, x[4], S24, 0xe7d3fbc8); /* 24 */
|
||||||
|
GG(a, b, c, d, x[9], S21, 0x21e1cde6); /* 25 */
|
||||||
|
GG(d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
|
||||||
|
GG(c, d, a, b, x[3], S23, 0xf4d50d87); /* 27 */
|
||||||
|
GG(b, c, d, a, x[8], S24, 0x455a14ed); /* 28 */
|
||||||
|
GG(a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
|
||||||
|
GG(d, a, b, c, x[2], S22, 0xfcefa3f8); /* 30 */
|
||||||
|
GG(c, d, a, b, x[7], S23, 0x676f02d9); /* 31 */
|
||||||
|
GG(b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
|
||||||
|
|
||||||
|
/* Round 3 */
|
||||||
|
HH(a, b, c, d, x[5], S31, 0xfffa3942); /* 33 */
|
||||||
|
HH(d, a, b, c, x[8], S32, 0x8771f681); /* 34 */
|
||||||
|
HH(c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
|
||||||
|
HH(b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
|
||||||
|
HH(a, b, c, d, x[1], S31, 0xa4beea44); /* 37 */
|
||||||
|
HH(d, a, b, c, x[4], S32, 0x4bdecfa9); /* 38 */
|
||||||
|
HH(c, d, a, b, x[7], S33, 0xf6bb4b60); /* 39 */
|
||||||
|
HH(b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
|
||||||
|
HH(a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
|
||||||
|
HH(d, a, b, c, x[0], S32, 0xeaa127fa); /* 42 */
|
||||||
|
HH(c, d, a, b, x[3], S33, 0xd4ef3085); /* 43 */
|
||||||
|
HH(b, c, d, a, x[6], S34, 0x4881d05); /* 44 */
|
||||||
|
HH(a, b, c, d, x[9], S31, 0xd9d4d039); /* 45 */
|
||||||
|
HH(d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
|
||||||
|
HH(c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
|
||||||
|
HH(b, c, d, a, x[2], S34, 0xc4ac5665); /* 48 */
|
||||||
|
|
||||||
|
/* Round 4 */
|
||||||
|
II(a, b, c, d, x[0], S41, 0xf4292244); /* 49 */
|
||||||
|
II(d, a, b, c, x[7], S42, 0x432aff97); /* 50 */
|
||||||
|
II(c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
|
||||||
|
II(b, c, d, a, x[5], S44, 0xfc93a039); /* 52 */
|
||||||
|
II(a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
|
||||||
|
II(d, a, b, c, x[3], S42, 0x8f0ccc92); /* 54 */
|
||||||
|
II(c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
|
||||||
|
II(b, c, d, a, x[1], S44, 0x85845dd1); /* 56 */
|
||||||
|
II(a, b, c, d, x[8], S41, 0x6fa87e4f); /* 57 */
|
||||||
|
II(d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
|
||||||
|
II(c, d, a, b, x[6], S43, 0xa3014314); /* 59 */
|
||||||
|
II(b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
|
||||||
|
II(a, b, c, d, x[4], S41, 0xf7537e82); /* 61 */
|
||||||
|
II(d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
|
||||||
|
II(c, d, a, b, x[2], S43, 0x2ad7d2bb); /* 63 */
|
||||||
|
II(b, c, d, a, x[9], S44, 0xeb86d391); /* 64 */
|
||||||
|
|
||||||
|
state[0] += a;
|
||||||
|
state[1] += b;
|
||||||
|
state[2] += c;
|
||||||
|
state[3] += d;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Zeroize sensitive information.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
MD5_memset((POINTER) x, 0, sizeof(x));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Encodes input (UINT4) into output (unsigned char). Assumes len is a
|
||||||
|
* multiple of 4.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
Encode(unsigned char *output, UINT4 *input, unsigned int len)
|
||||||
|
{
|
||||||
|
unsigned int i, j;
|
||||||
|
|
||||||
|
for (i = 0, j = 0; j < len; i++, j += 4) {
|
||||||
|
output[j] = (unsigned char) (input[i] & 0xff);
|
||||||
|
output[j + 1] = (unsigned char) ((input[i] >> 8) & 0xff);
|
||||||
|
output[j + 2] = (unsigned char) ((input[i] >> 16) & 0xff);
|
||||||
|
output[j + 3] = (unsigned char) ((input[i] >> 24) & 0xff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Decodes input (unsigned char) into output (UINT4). Assumes len is a
|
||||||
|
* multiple of 4.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
Decode(UINT4 *output, unsigned char *input, unsigned int len)
|
||||||
|
{
|
||||||
|
unsigned int i, j;
|
||||||
|
|
||||||
|
for (i = 0, j = 0; j < len; i++, j += 4)
|
||||||
|
output[i] = ((UINT4) input[j]) | (((UINT4) input[j + 1]) << 8) |
|
||||||
|
(((UINT4) input[j + 2]) << 16) | (((UINT4) input[j + 3]) << 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Note: Replace "for loop" with standard memcpy if possible.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void
|
||||||
|
MD5_memcpy(POINTER output, POINTER input, unsigned int len)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for (i = 0; i < len; i++)
|
||||||
|
output[i] = input[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Note: Replace "for loop" with standard memset if possible.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
MD5_memset(POINTER output, int value, unsigned int len)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for (i = 0; i < len; i++)
|
||||||
|
((char *) output)[i] = (char) value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Digests a string
|
||||||
|
*/
|
||||||
|
int my_md5_string(char *string,unsigned char digest[16])
|
||||||
|
{
|
||||||
|
MD5_CTX context;
|
||||||
|
unsigned int len = strlen(string);
|
||||||
|
|
||||||
|
my_md5_init(&context);
|
||||||
|
my_md5_update(&context, (unsigned char *)string, len);
|
||||||
|
my_md5_final(digest, &context);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int my_md5_buffer(char *buffer, unsigned int len, unsigned char digest[16])
|
||||||
|
{
|
||||||
|
MD5_CTX context;
|
||||||
|
|
||||||
|
my_md5_init(&context);
|
||||||
|
my_md5_update(&context, (unsigned char *)buffer, len);
|
||||||
|
my_md5_final(digest, &context);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int my_md5_file(char *filename,unsigned char digest[16])
|
||||||
|
{
|
||||||
|
FILE *file;
|
||||||
|
MD5_CTX context;
|
||||||
|
int len;
|
||||||
|
unsigned char buffer[1024];
|
||||||
|
|
||||||
|
if ((file = fopen(filename, "rb")) == NULL)
|
||||||
|
return -1;
|
||||||
|
else {
|
||||||
|
my_md5_init(&context);
|
||||||
|
while ((len = fread(buffer, 1, 1024, file)) > 0)
|
||||||
|
{
|
||||||
|
my_md5_update(&context, buffer, len);
|
||||||
|
}
|
||||||
|
my_md5_final(digest, &context);
|
||||||
|
|
||||||
|
fclose(file);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
#ifndef MCL_MD5_H
|
||||||
|
#define MCL_MD5_H
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
typedef unsigned char *POINTER;
|
||||||
|
typedef unsigned short int UINT2;
|
||||||
|
typedef unsigned int UINT4;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
UINT4 state[4]; /* state (ABCD) */
|
||||||
|
UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */
|
||||||
|
unsigned char buffer[64]; /* input buffer */
|
||||||
|
} MD5_CTX;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** md5 for string
|
||||||
|
* parameters:
|
||||||
|
* string: the string to md5
|
||||||
|
* digest: store the md5 digest
|
||||||
|
* return: 0 for success, != 0 fail
|
||||||
|
*/
|
||||||
|
int my_md5_string(char *string, unsigned char digest[16]);
|
||||||
|
|
||||||
|
/** md5 for file
|
||||||
|
* parameters:
|
||||||
|
* filename: the filename whose content to md5
|
||||||
|
* digest: store the md5 digest
|
||||||
|
* return: 0 for success, != 0 fail
|
||||||
|
*/
|
||||||
|
int my_md5_file(char *filename, unsigned char digest[16]);
|
||||||
|
|
||||||
|
/** md5 for buffer
|
||||||
|
* parameters:
|
||||||
|
* buffer: the buffer to md5
|
||||||
|
* len: the buffer length
|
||||||
|
* digest: store the md5 digest
|
||||||
|
* return: 0 for success, != 0 fail
|
||||||
|
*/
|
||||||
|
int my_md5_buffer(char *buffer, unsigned int len, unsigned char digest[16]);
|
||||||
|
|
||||||
|
void my_md5_init (MD5_CTX *);
|
||||||
|
|
||||||
|
void my_md5_update (MD5_CTX *, unsigned char *, unsigned int);
|
||||||
|
|
||||||
|
void my_md5_final (unsigned char [16], MD5_CTX *);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
@ -0,0 +1,131 @@
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2008 Happy Fish / YuQing
|
||||||
|
*
|
||||||
|
* FastDFS may be copied only under the terms of the GNU General
|
||||||
|
* Public License V3, which may be found in the FastDFS source kit.
|
||||||
|
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||||
|
**/
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include "logger.h"
|
||||||
|
#include "http_func.h"
|
||||||
|
#include "shared_func.h"
|
||||||
|
#include "mime_file_parser.h"
|
||||||
|
|
||||||
|
int load_mime_types_from_file(HashArray *pHash, const char *mime_filename)
|
||||||
|
{
|
||||||
|
#define MIME_DELIM_CHARS " \t"
|
||||||
|
|
||||||
|
int result;
|
||||||
|
char *content;
|
||||||
|
char *pLine;
|
||||||
|
char *pLastEnd;
|
||||||
|
char *content_type;
|
||||||
|
char *ext_name;
|
||||||
|
char *lasts;
|
||||||
|
int http_status;
|
||||||
|
int content_len;
|
||||||
|
int64_t file_size;
|
||||||
|
char error_info[512];
|
||||||
|
|
||||||
|
if (strncasecmp(mime_filename, "http://", 7) == 0)
|
||||||
|
{
|
||||||
|
if ((result=get_url_content(mime_filename, 30, 60, &http_status,\
|
||||||
|
&content, &content_len, error_info)) != 0)
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"get_url_content fail, " \
|
||||||
|
"url: %s, error info: %s", \
|
||||||
|
__LINE__, mime_filename, error_info);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (http_status != 200)
|
||||||
|
{
|
||||||
|
free(content);
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"HTTP status code: %d != 200, url: %s", \
|
||||||
|
__LINE__, http_status, mime_filename);
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ((result=getFileContent(mime_filename, &content, \
|
||||||
|
&file_size)) != 0)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((result=hash_init_ex(pHash, PJWHash, 2 * 1024, 0.75, 0, true)) != 0)
|
||||||
|
{
|
||||||
|
free(content);
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"hash_init_ex fail, errno: %d, error info: %s", \
|
||||||
|
__LINE__, result, STRERROR(result));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
pLastEnd = content - 1;
|
||||||
|
while (pLastEnd != NULL)
|
||||||
|
{
|
||||||
|
pLine = pLastEnd + 1;
|
||||||
|
pLastEnd = strchr(pLine, '\n');
|
||||||
|
if (pLastEnd != NULL)
|
||||||
|
{
|
||||||
|
*pLastEnd = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*pLine == '\0' || *pLine == '#')
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
lasts = NULL;
|
||||||
|
content_type = strtok_r(pLine, MIME_DELIM_CHARS, &lasts);
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
ext_name = strtok_r(NULL, MIME_DELIM_CHARS, &lasts);
|
||||||
|
if (ext_name == NULL)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*ext_name == '\0')
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((result=hash_insert_ex(pHash, ext_name, \
|
||||||
|
strlen(ext_name)+1, content_type, \
|
||||||
|
strlen(content_type)+1, true)) < 0)
|
||||||
|
{
|
||||||
|
free(content);
|
||||||
|
result *= -1;
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"hash_insert_ex fail, errno: %d, " \
|
||||||
|
"error info: %s", __LINE__, \
|
||||||
|
result, STRERROR(result));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(content);
|
||||||
|
|
||||||
|
//hash_stat_print(pHash);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2008 Happy Fish / YuQing
|
||||||
|
*
|
||||||
|
* FastDFS may be copied only under the terms of the GNU General
|
||||||
|
* Public License V3, which may be found in the FastDFS source kit.
|
||||||
|
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||||
|
**/
|
||||||
|
|
||||||
|
#ifndef _MINE_FILE_PARSER_H
|
||||||
|
#define _MINE_FILE_PARSER_H
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include "hash.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
load mime types from file
|
||||||
|
params:
|
||||||
|
pHash: hash array to store the mime types,
|
||||||
|
key is the file extension name, eg. jpg
|
||||||
|
value is the content type, eg. image/jpeg
|
||||||
|
the hash array will be initialized in this function,
|
||||||
|
the hash array should be destroyed when used done
|
||||||
|
mime_filename: the mime filename,
|
||||||
|
file format is same as apache's file: mime.types
|
||||||
|
return: 0 for success, !=0 for fail
|
||||||
|
**/
|
||||||
|
int load_mime_types_from_file(HashArray *pHash, const char *mime_filename);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
@ -0,0 +1,234 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include "shared_func.h"
|
||||||
|
#include "logger.h"
|
||||||
|
#include "process_ctrl.h"
|
||||||
|
|
||||||
|
int get_pid_from_file(const char *pidFilename, pid_t *pid)
|
||||||
|
{
|
||||||
|
char buff[32];
|
||||||
|
int64_t file_size;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
if (access(pidFilename, F_OK) != 0) {
|
||||||
|
return errno != 0 ? errno : EPERM;
|
||||||
|
}
|
||||||
|
|
||||||
|
file_size = sizeof(buff) - 1;
|
||||||
|
if ((result=getFileContentEx(pidFilename, buff, 0, &file_size)) != 0) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
*(buff + file_size) = '\0';
|
||||||
|
*pid = strtol(buff, NULL, 10);
|
||||||
|
if (*pid == 0) {
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int write_to_pid_file(const char *pidFilename)
|
||||||
|
{
|
||||||
|
char buff[32];
|
||||||
|
int len;
|
||||||
|
|
||||||
|
len = sprintf(buff, "%d", (int)getpid());
|
||||||
|
return writeToFile(pidFilename, buff, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
int delete_pid_file(const char *pidFilename)
|
||||||
|
{
|
||||||
|
if (unlink(pidFilename) == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return errno != 0 ? errno : ENOENT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int do_stop(const char *pidFilename, const bool bShowError, pid_t *pid)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
|
||||||
|
if ((result=get_pid_from_file(pidFilename, pid)) != 0) {
|
||||||
|
if (bShowError) {
|
||||||
|
if (result == ENOENT) {
|
||||||
|
fprintf(stderr, "pid file: %s not exist!\n", pidFilename);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
fprintf(stderr, "get pid from file: %s fail, " \
|
||||||
|
"errno: %d, error info: %s\n",
|
||||||
|
pidFilename, result, strerror(result));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (kill(*pid, SIGTERM) == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
result = errno != 0 ? errno : EPERM;
|
||||||
|
if (bShowError || result != ESRCH) {
|
||||||
|
fprintf(stderr, "kill pid: %d fail, errno: %d, error info: %s\n",
|
||||||
|
(int)*pid, result, strerror(result));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int process_stop(const char *pidFilename)
|
||||||
|
{
|
||||||
|
pid_t pid;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
result = do_stop(pidFilename, true, &pid);
|
||||||
|
if (result != 0) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "waiting for pid [%d] exit ...\n", (int)pid);
|
||||||
|
do {
|
||||||
|
sleep(1);
|
||||||
|
} while (kill(pid, SIGTERM) == 0);
|
||||||
|
fprintf(stderr, "pid [%d] exit.\n", (int)pid);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int process_restart(const char *pidFilename)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
pid_t pid;
|
||||||
|
|
||||||
|
result = do_stop(pidFilename, false, &pid);
|
||||||
|
if (result == 0) {
|
||||||
|
fprintf(stderr, "waiting for pid [%d] exit ...\n", (int)pid);
|
||||||
|
do {
|
||||||
|
sleep(1);
|
||||||
|
} while (kill(pid, SIGTERM) == 0);
|
||||||
|
fprintf(stderr, "starting ...\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result == ENOENT || result == ESRCH) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int process_exist(const char *pidFilename)
|
||||||
|
{
|
||||||
|
pid_t pid;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
if ((result=get_pid_from_file(pidFilename, &pid)) != 0) {
|
||||||
|
if (result == ENOENT) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
fprintf(stderr, "get pid from file: %s fail, " \
|
||||||
|
"errno: %d, error info: %s\n",
|
||||||
|
pidFilename, result, strerror(result));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (kill(pid, 0) == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (errno == ENOENT || errno == ESRCH) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
fprintf(stderr, "kill pid: %d fail, errno: %d, error info: %s\n",
|
||||||
|
(int)pid, errno, strerror(errno));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_base_path_from_conf_file(const char *filename, char *base_path,
|
||||||
|
const int path_size)
|
||||||
|
{
|
||||||
|
char *pBasePath;
|
||||||
|
IniContext iniContext;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
memset(&iniContext, 0, sizeof(IniContext));
|
||||||
|
if ((result=iniLoadFromFile(filename, &iniContext)) != 0)
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"load conf file \"%s\" fail, ret code: %d", \
|
||||||
|
__LINE__, filename, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
pBasePath = iniGetStrValue(NULL, "base_path", &iniContext);
|
||||||
|
if (pBasePath == NULL)
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"conf file \"%s\" must have item " \
|
||||||
|
"\"base_path\"!", __LINE__, filename);
|
||||||
|
result = ENOENT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(base_path, path_size, "%s", pBasePath);
|
||||||
|
chopPath(base_path);
|
||||||
|
if (!fileExists(base_path))
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"\"%s\" can't be accessed, error info: %s", \
|
||||||
|
__LINE__, base_path, STRERROR(errno));
|
||||||
|
result = errno != 0 ? errno : ENOENT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!isDir(base_path))
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"\"%s\" is not a directory!", \
|
||||||
|
__LINE__, base_path);
|
||||||
|
result = ENOTDIR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (0);
|
||||||
|
|
||||||
|
iniFreeContext(&iniContext);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int process_action(const char *pidFilename, const char *action, bool *stop)
|
||||||
|
{
|
||||||
|
*stop = false;
|
||||||
|
if (action == NULL)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(action, "stop") == 0)
|
||||||
|
{
|
||||||
|
*stop = true;
|
||||||
|
return process_stop(pidFilename);
|
||||||
|
}
|
||||||
|
else if (strcmp(action, "restart") == 0)
|
||||||
|
{
|
||||||
|
return process_restart(pidFilename);
|
||||||
|
}
|
||||||
|
else if (strcmp(action, "start") == 0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf(stderr, "invalid action: %s\n", action);
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
|
||||||
|
#ifndef PROCESS_CTRL_H
|
||||||
|
#define PROCESS_CTRL_H
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <sys/resource.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int get_base_path_from_conf_file(const char *filename, char *base_path,
|
||||||
|
const int path_size);
|
||||||
|
|
||||||
|
int get_pid_from_file(const char *pidFilename, pid_t *pid);
|
||||||
|
|
||||||
|
int write_to_pid_file(const char *pidFilename);
|
||||||
|
|
||||||
|
int delete_pid_file(const char *pidFilename);
|
||||||
|
|
||||||
|
int process_stop(const char *pidFilename);
|
||||||
|
|
||||||
|
int process_restart(const char *pidFilename);
|
||||||
|
|
||||||
|
int process_exist(const char *pidFilename);
|
||||||
|
|
||||||
|
int process_action(const char *pidFilename, const char *action, bool *stop);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
@ -0,0 +1,193 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2008 Happy Fish / YuQing
|
||||||
|
*
|
||||||
|
* FastDFS may be copied only under the terms of the GNU General
|
||||||
|
* Public License V3, which may be found in the FastDFS source kit.
|
||||||
|
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||||
|
**/
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/file.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <grp.h>
|
||||||
|
#include <pwd.h>
|
||||||
|
#include "pthread_func.h"
|
||||||
|
#include "logger.h"
|
||||||
|
|
||||||
|
int init_pthread_lock(pthread_mutex_t *pthread_lock)
|
||||||
|
{
|
||||||
|
pthread_mutexattr_t mat;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
if ((result=pthread_mutexattr_init(&mat)) != 0)
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"call pthread_mutexattr_init fail, " \
|
||||||
|
"errno: %d, error info: %s", \
|
||||||
|
__LINE__, result, STRERROR(result));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if ((result=pthread_mutexattr_settype(&mat, \
|
||||||
|
PTHREAD_MUTEX_ERRORCHECK)) != 0)
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"call pthread_mutexattr_settype fail, " \
|
||||||
|
"errno: %d, error info: %s", \
|
||||||
|
__LINE__, result, STRERROR(result));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if ((result=pthread_mutex_init(pthread_lock, &mat)) != 0)
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"call pthread_mutex_init fail, " \
|
||||||
|
"errno: %d, error info: %s", \
|
||||||
|
__LINE__, result, STRERROR(result));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if ((result=pthread_mutexattr_destroy(&mat)) != 0)
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"call thread_mutexattr_destroy fail, " \
|
||||||
|
"errno: %d, error info: %s", \
|
||||||
|
__LINE__, result, STRERROR(result));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int init_pthread_attr(pthread_attr_t *pattr, const int stack_size)
|
||||||
|
{
|
||||||
|
size_t old_stack_size;
|
||||||
|
size_t new_stack_size;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
if ((result=pthread_attr_init(pattr)) != 0)
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"call pthread_attr_init fail, " \
|
||||||
|
"errno: %d, error info: %s", \
|
||||||
|
__LINE__, result, STRERROR(result));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((result=pthread_attr_getstacksize(pattr, &old_stack_size)) != 0)
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"call pthread_attr_getstacksize fail, " \
|
||||||
|
"errno: %d, error info: %s", \
|
||||||
|
__LINE__, result, STRERROR(result));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stack_size > 0)
|
||||||
|
{
|
||||||
|
if (old_stack_size != stack_size)
|
||||||
|
{
|
||||||
|
new_stack_size = stack_size;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
new_stack_size = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (old_stack_size < 1 * 1024 * 1024)
|
||||||
|
{
|
||||||
|
new_stack_size = 1 * 1024 * 1024;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
new_stack_size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new_stack_size > 0)
|
||||||
|
{
|
||||||
|
if ((result=pthread_attr_setstacksize(pattr, \
|
||||||
|
new_stack_size)) != 0)
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"call pthread_attr_setstacksize fail, " \
|
||||||
|
"errno: %d, error info: %s", \
|
||||||
|
__LINE__, result, STRERROR(result));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((result=pthread_attr_setdetachstate(pattr, \
|
||||||
|
PTHREAD_CREATE_DETACHED)) != 0)
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"call pthread_attr_setdetachstate fail, " \
|
||||||
|
"errno: %d, error info: %s", \
|
||||||
|
__LINE__, result, STRERROR(result));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int create_work_threads(int *count, void *(*start_func)(void *), \
|
||||||
|
void *arg, pthread_t *tids, const int stack_size)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
pthread_attr_t thread_attr;
|
||||||
|
pthread_t *ptid;
|
||||||
|
pthread_t *ptid_end;
|
||||||
|
|
||||||
|
if ((result=init_pthread_attr(&thread_attr, stack_size)) != 0)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = 0;
|
||||||
|
ptid_end = tids + (*count);
|
||||||
|
for (ptid=tids; ptid<ptid_end; ptid++)
|
||||||
|
{
|
||||||
|
if ((result=pthread_create(ptid, &thread_attr, \
|
||||||
|
start_func, arg)) != 0)
|
||||||
|
{
|
||||||
|
*count = ptid - tids;
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"create thread failed, startup threads: %d, " \
|
||||||
|
"errno: %d, error info: %s", \
|
||||||
|
__LINE__, *count, \
|
||||||
|
result, STRERROR(result));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_attr_destroy(&thread_attr);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int kill_work_threads(pthread_t *tids, const int count)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
pthread_t *ptid;
|
||||||
|
pthread_t *ptid_end;
|
||||||
|
|
||||||
|
ptid_end = tids + count;
|
||||||
|
for (ptid=tids; ptid<ptid_end; ptid++)
|
||||||
|
{
|
||||||
|
if ((result=pthread_kill(*ptid, SIGINT)) != 0)
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"kill thread failed, " \
|
||||||
|
"errno: %d, error info: %s", \
|
||||||
|
__LINE__, result, STRERROR(result));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2008 Happy Fish / YuQing
|
||||||
|
*
|
||||||
|
* FastDFS may be copied only under the terms of the GNU General
|
||||||
|
* Public License V3, which may be found in the FastDFS source kit.
|
||||||
|
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||||
|
**/
|
||||||
|
|
||||||
|
#ifndef PTHREAD_FUNC_H
|
||||||
|
#define PTHREAD_FUNC_H
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <sys/resource.h>
|
||||||
|
#include "common_define.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int init_pthread_lock(pthread_mutex_t *pthread_lock);
|
||||||
|
int init_pthread_attr(pthread_attr_t *pattr, const int stack_size);
|
||||||
|
|
||||||
|
int create_work_threads(int *count, void *(*start_func)(void *), \
|
||||||
|
void *arg, pthread_t *tids, const int stack_size);
|
||||||
|
int kill_work_threads(pthread_t *tids, const int count);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
@ -0,0 +1,508 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2008 Happy Fish / YuQing
|
||||||
|
*
|
||||||
|
* FastDFS may be copied only under the terms of the GNU General
|
||||||
|
* Public License V3, which may be found in the FastDFS source kit.
|
||||||
|
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||||
|
**/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include "sched_thread.h"
|
||||||
|
#include "shared_func.h"
|
||||||
|
#include "pthread_func.h"
|
||||||
|
#include "logger.h"
|
||||||
|
|
||||||
|
volatile bool g_schedule_flag = false;
|
||||||
|
volatile time_t g_current_time = 0;
|
||||||
|
|
||||||
|
static ScheduleArray waiting_schedule_array = {NULL, 0};
|
||||||
|
static int waiting_del_id = -1;
|
||||||
|
|
||||||
|
static int sched_cmp_by_next_call_time(const void *p1, const void *p2)
|
||||||
|
{
|
||||||
|
return ((ScheduleEntry *)p1)->next_call_time - \
|
||||||
|
((ScheduleEntry *)p2)->next_call_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sched_init_entries(ScheduleArray *pScheduleArray)
|
||||||
|
{
|
||||||
|
ScheduleEntry *pEntry;
|
||||||
|
ScheduleEntry *pEnd;
|
||||||
|
time_t time_base;
|
||||||
|
struct tm tm_current;
|
||||||
|
struct tm tm_base;
|
||||||
|
|
||||||
|
if (pScheduleArray->count < 0)
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"schedule count %d < 0", \
|
||||||
|
__LINE__, pScheduleArray->count);
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
if (pScheduleArray->count == 0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_current_time = time(NULL);
|
||||||
|
localtime_r((time_t *)&g_current_time, &tm_current);
|
||||||
|
pEnd = pScheduleArray->entries + pScheduleArray->count;
|
||||||
|
for (pEntry=pScheduleArray->entries; pEntry<pEnd; pEntry++)
|
||||||
|
{
|
||||||
|
if (pEntry->interval <= 0)
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"shedule interval %d <= 0", \
|
||||||
|
__LINE__, pEntry->interval);
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pEntry->time_base.hour == TIME_NONE)
|
||||||
|
{
|
||||||
|
pEntry->next_call_time = g_current_time + \
|
||||||
|
pEntry->interval;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (tm_current.tm_hour > pEntry->time_base.hour || \
|
||||||
|
(tm_current.tm_hour == pEntry->time_base.hour \
|
||||||
|
&& tm_current.tm_min >= pEntry->time_base.minute))
|
||||||
|
{
|
||||||
|
memcpy(&tm_base, &tm_current, sizeof(struct tm));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
time_base = g_current_time - 24 * 3600;
|
||||||
|
localtime_r(&time_base, &tm_base);
|
||||||
|
}
|
||||||
|
|
||||||
|
tm_base.tm_hour = pEntry->time_base.hour;
|
||||||
|
tm_base.tm_min = pEntry->time_base.minute;
|
||||||
|
tm_base.tm_sec = 0;
|
||||||
|
time_base = mktime(&tm_base);
|
||||||
|
|
||||||
|
pEntry->next_call_time = g_current_time + \
|
||||||
|
pEntry->interval - (g_current_time - \
|
||||||
|
time_base) % pEntry->interval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
{
|
||||||
|
char buff1[32];
|
||||||
|
char buff2[32];
|
||||||
|
logInfo("id=%d, current time=%s, first call time=%s\n", \
|
||||||
|
pEntry->id, formatDatetime(g_current_time, \
|
||||||
|
"%Y-%m-%d %H:%M:%S", buff1, sizeof(buff1)), \
|
||||||
|
formatDatetime(pEntry->next_call_time, \
|
||||||
|
"%Y-%m-%d %H:%M:%S", buff2, sizeof(buff2)));
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sched_make_chain(ScheduleContext *pContext)
|
||||||
|
{
|
||||||
|
ScheduleArray *pScheduleArray;
|
||||||
|
ScheduleEntry *pEntry;
|
||||||
|
|
||||||
|
pScheduleArray = &(pContext->scheduleArray);
|
||||||
|
if (pScheduleArray->count == 0)
|
||||||
|
{
|
||||||
|
pContext->head = NULL;
|
||||||
|
pContext->tail = NULL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
qsort(pScheduleArray->entries, pScheduleArray->count, \
|
||||||
|
sizeof(ScheduleEntry), sched_cmp_by_next_call_time);
|
||||||
|
|
||||||
|
pContext->head = pScheduleArray->entries;
|
||||||
|
pContext->tail = pScheduleArray->entries + (pScheduleArray->count - 1);
|
||||||
|
for (pEntry=pScheduleArray->entries; pEntry<pContext->tail; pEntry++)
|
||||||
|
{
|
||||||
|
pEntry->next = pEntry + 1;
|
||||||
|
}
|
||||||
|
pContext->tail->next = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sched_check_waiting(ScheduleContext *pContext)
|
||||||
|
{
|
||||||
|
ScheduleArray *pScheduleArray;
|
||||||
|
ScheduleEntry *newEntries;
|
||||||
|
ScheduleEntry *pWaitingEntry;
|
||||||
|
ScheduleEntry *pWaitingEnd;
|
||||||
|
ScheduleEntry *pSchedEntry;
|
||||||
|
ScheduleEntry *pSchedEnd;
|
||||||
|
int allocCount;
|
||||||
|
int newCount;
|
||||||
|
int result;
|
||||||
|
int deleteCount;
|
||||||
|
|
||||||
|
pScheduleArray = &(pContext->scheduleArray);
|
||||||
|
deleteCount = 0;
|
||||||
|
if (waiting_del_id >= 0)
|
||||||
|
{
|
||||||
|
pSchedEnd = pScheduleArray->entries + pScheduleArray->count;
|
||||||
|
for (pSchedEntry=pScheduleArray->entries; \
|
||||||
|
pSchedEntry<pSchedEnd; pSchedEntry++)
|
||||||
|
{
|
||||||
|
if (pSchedEntry->id == waiting_del_id)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pSchedEntry < pSchedEnd)
|
||||||
|
{
|
||||||
|
pSchedEntry++;
|
||||||
|
while (pSchedEntry < pSchedEnd)
|
||||||
|
{
|
||||||
|
memcpy(pSchedEntry - 1, pSchedEntry, \
|
||||||
|
sizeof(ScheduleEntry));
|
||||||
|
pSchedEntry++;
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteCount++;
|
||||||
|
pScheduleArray->count--;
|
||||||
|
|
||||||
|
logDebug("file: "__FILE__", line: %d, " \
|
||||||
|
"delete task id: %d, " \
|
||||||
|
"current schedule count: %d", __LINE__, \
|
||||||
|
waiting_del_id, pScheduleArray->count);
|
||||||
|
}
|
||||||
|
|
||||||
|
waiting_del_id = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (waiting_schedule_array.count == 0)
|
||||||
|
{
|
||||||
|
if (deleteCount > 0)
|
||||||
|
{
|
||||||
|
sched_make_chain(pContext);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
allocCount = pScheduleArray->count + waiting_schedule_array.count;
|
||||||
|
newEntries = (ScheduleEntry *)malloc(sizeof(ScheduleEntry) * allocCount);
|
||||||
|
if (newEntries == NULL)
|
||||||
|
{
|
||||||
|
result = errno != 0 ? errno : ENOMEM;
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"malloc %d bytes failed, " \
|
||||||
|
"errno: %d, error info: %s", \
|
||||||
|
__LINE__, (int)sizeof(ScheduleEntry) * allocCount, \
|
||||||
|
result, STRERROR(result));
|
||||||
|
|
||||||
|
if (deleteCount > 0)
|
||||||
|
{
|
||||||
|
sched_make_chain(pContext);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pScheduleArray->count > 0)
|
||||||
|
{
|
||||||
|
memcpy(newEntries, pScheduleArray->entries, \
|
||||||
|
sizeof(ScheduleEntry) * pScheduleArray->count);
|
||||||
|
}
|
||||||
|
newCount = pScheduleArray->count;
|
||||||
|
pWaitingEnd = waiting_schedule_array.entries + waiting_schedule_array.count;
|
||||||
|
for (pWaitingEntry=waiting_schedule_array.entries; \
|
||||||
|
pWaitingEntry<pWaitingEnd; pWaitingEntry++)
|
||||||
|
{
|
||||||
|
pSchedEnd = newEntries + newCount;
|
||||||
|
for (pSchedEntry=newEntries; pSchedEntry<pSchedEnd; \
|
||||||
|
pSchedEntry++)
|
||||||
|
{
|
||||||
|
if (pWaitingEntry->id == pSchedEntry->id)
|
||||||
|
{
|
||||||
|
memcpy(pSchedEntry, pWaitingEntry, \
|
||||||
|
sizeof(ScheduleEntry));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pSchedEntry == pSchedEnd)
|
||||||
|
{
|
||||||
|
memcpy(pSchedEntry, pWaitingEntry, \
|
||||||
|
sizeof(ScheduleEntry));
|
||||||
|
newCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logDebug("file: "__FILE__", line: %d, " \
|
||||||
|
"schedule add entries: %d, replace entries: %d",
|
||||||
|
__LINE__, newCount - pScheduleArray->count, \
|
||||||
|
waiting_schedule_array.count - (newCount - pScheduleArray->count));
|
||||||
|
|
||||||
|
if (pScheduleArray->entries != NULL)
|
||||||
|
{
|
||||||
|
free(pScheduleArray->entries);
|
||||||
|
}
|
||||||
|
pScheduleArray->entries = newEntries;
|
||||||
|
pScheduleArray->count = newCount;
|
||||||
|
|
||||||
|
free(waiting_schedule_array.entries);
|
||||||
|
waiting_schedule_array.count = 0;
|
||||||
|
waiting_schedule_array.entries = NULL;
|
||||||
|
|
||||||
|
sched_make_chain(pContext);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *sched_thread_entrance(void *args)
|
||||||
|
{
|
||||||
|
ScheduleContext *pContext;
|
||||||
|
ScheduleEntry *pPrevious;
|
||||||
|
ScheduleEntry *pCurrent;
|
||||||
|
ScheduleEntry *pSaveNext;
|
||||||
|
ScheduleEntry *pNode;
|
||||||
|
ScheduleEntry *pUntil;
|
||||||
|
int exec_count;
|
||||||
|
int i;
|
||||||
|
int sleep_time;
|
||||||
|
|
||||||
|
pContext = (ScheduleContext *)args;
|
||||||
|
if (sched_init_entries(&(pContext->scheduleArray)) != 0)
|
||||||
|
{
|
||||||
|
free(pContext);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
sched_make_chain(pContext);
|
||||||
|
|
||||||
|
g_schedule_flag = true;
|
||||||
|
while (*(pContext->pcontinue_flag))
|
||||||
|
{
|
||||||
|
sched_check_waiting(pContext);
|
||||||
|
if (pContext->scheduleArray.count == 0) //no schedule entry
|
||||||
|
{
|
||||||
|
sleep(1);
|
||||||
|
g_current_time = time(NULL);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_current_time = time(NULL);
|
||||||
|
sleep_time = pContext->head->next_call_time - g_current_time;
|
||||||
|
|
||||||
|
/*
|
||||||
|
//fprintf(stderr, "count=%d, sleep_time=%d\n", \
|
||||||
|
pContext->scheduleArray.count, sleep_time);
|
||||||
|
*/
|
||||||
|
while (sleep_time > 0 && *(pContext->pcontinue_flag))
|
||||||
|
{
|
||||||
|
sleep(1);
|
||||||
|
g_current_time = time(NULL);
|
||||||
|
if (sched_check_waiting(pContext) == 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
sleep_time--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(*(pContext->pcontinue_flag)))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
exec_count = 0;
|
||||||
|
pCurrent = pContext->head;
|
||||||
|
while (*(pContext->pcontinue_flag) && (pCurrent != NULL \
|
||||||
|
&& pCurrent->next_call_time <= g_current_time))
|
||||||
|
{
|
||||||
|
//fprintf(stderr, "exec task id=%d\n", pCurrent->id);
|
||||||
|
pCurrent->task_func(pCurrent->func_args);
|
||||||
|
pCurrent->next_call_time = g_current_time + \
|
||||||
|
pCurrent->interval;
|
||||||
|
pCurrent = pCurrent->next;
|
||||||
|
exec_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exec_count == 0 || pContext->scheduleArray.count == 1)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exec_count > pContext->scheduleArray.count / 2)
|
||||||
|
{
|
||||||
|
sched_make_chain(pContext);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
pNode = pContext->head;
|
||||||
|
pContext->head = pCurrent; //new chain head
|
||||||
|
for (i=0; i<exec_count; i++)
|
||||||
|
{
|
||||||
|
if (pNode->next_call_time >= pContext->tail->next_call_time)
|
||||||
|
{
|
||||||
|
pContext->tail->next = pNode;
|
||||||
|
pContext->tail = pNode;
|
||||||
|
pNode = pNode->next;
|
||||||
|
pContext->tail->next = NULL;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
pPrevious = NULL;
|
||||||
|
pUntil = pContext->head;
|
||||||
|
while (pUntil != NULL && \
|
||||||
|
pNode->next_call_time > pUntil->next_call_time)
|
||||||
|
{
|
||||||
|
pPrevious = pUntil;
|
||||||
|
pUntil = pUntil->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
pSaveNext = pNode->next;
|
||||||
|
if (pPrevious == NULL)
|
||||||
|
{
|
||||||
|
pContext->head = pNode;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pPrevious->next = pNode;
|
||||||
|
}
|
||||||
|
pNode->next = pUntil;
|
||||||
|
|
||||||
|
pNode = pSaveNext;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
g_schedule_flag = false;
|
||||||
|
|
||||||
|
logDebug("file: "__FILE__", line: %d, " \
|
||||||
|
"schedule thread exit", __LINE__);
|
||||||
|
|
||||||
|
free(pContext);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sched_dup_array(const ScheduleArray *pSrcArray, \
|
||||||
|
ScheduleArray *pDestArray)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
int bytes;
|
||||||
|
|
||||||
|
if (pSrcArray->count == 0)
|
||||||
|
{
|
||||||
|
pDestArray->entries = NULL;
|
||||||
|
pDestArray->count = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes = sizeof(ScheduleEntry) * pSrcArray->count;
|
||||||
|
pDestArray->entries = (ScheduleEntry *)malloc(bytes);
|
||||||
|
if (pDestArray->entries == NULL)
|
||||||
|
{
|
||||||
|
result = errno != 0 ? errno : ENOMEM;
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"malloc %d bytes failed, " \
|
||||||
|
"errno: %d, error info: %s", \
|
||||||
|
__LINE__, bytes, result, STRERROR(result));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(pDestArray->entries, pSrcArray->entries, bytes);
|
||||||
|
pDestArray->count = pSrcArray->count;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sched_add_entries(const ScheduleArray *pScheduleArray)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
|
||||||
|
if (pScheduleArray->count == 0)
|
||||||
|
{
|
||||||
|
logDebug("file: "__FILE__", line: %d, " \
|
||||||
|
"no schedule entry", __LINE__);
|
||||||
|
return ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (waiting_schedule_array.entries != NULL)
|
||||||
|
{
|
||||||
|
logDebug("file: "__FILE__", line: %d, " \
|
||||||
|
"waiting for schedule array ready ...", __LINE__);
|
||||||
|
sleep(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((result=sched_dup_array(pScheduleArray, &waiting_schedule_array))!=0)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sched_init_entries(&waiting_schedule_array);
|
||||||
|
}
|
||||||
|
|
||||||
|
int sched_del_entry(const int id)
|
||||||
|
{
|
||||||
|
if (id < 0)
|
||||||
|
{
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"id: %d is invalid!", __LINE__, id);
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (waiting_del_id >= 0)
|
||||||
|
{
|
||||||
|
logDebug("file: "__FILE__", line: %d, " \
|
||||||
|
"waiting for delete ready ...", __LINE__);
|
||||||
|
sleep(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
waiting_del_id = id;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sched_start(ScheduleArray *pScheduleArray, pthread_t *ptid, \
|
||||||
|
const int stack_size, bool * volatile pcontinue_flag)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
pthread_attr_t thread_attr;
|
||||||
|
ScheduleContext *pContext;
|
||||||
|
|
||||||
|
pContext = (ScheduleContext *)malloc(sizeof(ScheduleContext));
|
||||||
|
if (pContext == NULL)
|
||||||
|
{
|
||||||
|
result = errno != 0 ? errno : ENOMEM;
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"malloc %d bytes failed, " \
|
||||||
|
"errno: %d, error info: %s", \
|
||||||
|
__LINE__, (int)sizeof(ScheduleContext), \
|
||||||
|
result, STRERROR(result));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((result=init_pthread_attr(&thread_attr, stack_size)) != 0)
|
||||||
|
{
|
||||||
|
free(pContext);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((result=sched_dup_array(pScheduleArray, \
|
||||||
|
&(pContext->scheduleArray))) != 0)
|
||||||
|
{
|
||||||
|
free(pContext);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
pContext->pcontinue_flag = pcontinue_flag;
|
||||||
|
if ((result=pthread_create(ptid, &thread_attr, \
|
||||||
|
sched_thread_entrance, pContext)) != 0)
|
||||||
|
{
|
||||||
|
free(pContext);
|
||||||
|
logError("file: "__FILE__", line: %d, " \
|
||||||
|
"create thread failed, " \
|
||||||
|
"errno: %d, error info: %s", \
|
||||||
|
__LINE__, result, STRERROR(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_attr_destroy(&thread_attr);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,78 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2008 Happy Fish / YuQing
|
||||||
|
*
|
||||||
|
* FastDFS may be copied only under the terms of the GNU General
|
||||||
|
* Public License V3, which may be found in the FastDFS source kit.
|
||||||
|
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||||
|
**/
|
||||||
|
|
||||||
|
#ifndef _SCHED_THREAD_H_
|
||||||
|
#define _SCHED_THREAD_H_
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
#include "common_define.h"
|
||||||
|
|
||||||
|
typedef int (*TaskFunc) (void *args);
|
||||||
|
|
||||||
|
typedef struct tagScheduleEntry
|
||||||
|
{
|
||||||
|
int id; //the task id
|
||||||
|
|
||||||
|
/* the time base to execute task, such as 00:00, interval is 3600,
|
||||||
|
means execute the task every hour as 1:00, 2:00, 3:00 etc. */
|
||||||
|
TimeInfo time_base;
|
||||||
|
|
||||||
|
int interval; //the interval for execute task, unit is second
|
||||||
|
|
||||||
|
TaskFunc task_func; //callback function
|
||||||
|
void *func_args; //arguments pass to callback function
|
||||||
|
|
||||||
|
/* following are internal fields, do not set manually! */
|
||||||
|
time_t next_call_time;
|
||||||
|
struct tagScheduleEntry *next;
|
||||||
|
} ScheduleEntry;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
ScheduleEntry *entries;
|
||||||
|
int count;
|
||||||
|
} ScheduleArray;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
ScheduleArray scheduleArray;
|
||||||
|
ScheduleEntry *head; //schedule chain head
|
||||||
|
ScheduleEntry *tail; //schedule chain tail
|
||||||
|
bool *pcontinue_flag;
|
||||||
|
} ScheduleContext;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern volatile bool g_schedule_flag; //schedule continue running flag
|
||||||
|
extern volatile time_t g_current_time; //the current time
|
||||||
|
|
||||||
|
|
||||||
|
#define get_current_time() (g_schedule_flag ? g_current_time: time(NULL))
|
||||||
|
|
||||||
|
int sched_add_entries(const ScheduleArray *pScheduleArray);
|
||||||
|
int sched_del_entry(const int id);
|
||||||
|
|
||||||
|
/** execute the schedule thread
|
||||||
|
* parameters:
|
||||||
|
* pScheduleArray: schedule task
|
||||||
|
* ptid: store the schedule thread id
|
||||||
|
* stack_size: set thread stack size (byes)
|
||||||
|
* pcontinue_flag: main process continue running flag
|
||||||
|
* return: error no, 0 for success, != 0 fail
|
||||||
|
*/
|
||||||
|
int sched_start(ScheduleArray *pScheduleArray, pthread_t *ptid, \
|
||||||
|
const int stack_size, bool * volatile pcontinue_flag);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,495 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2008 Happy Fish / YuQing
|
||||||
|
*
|
||||||
|
* FastDFS may be copied only under the terms of the GNU General
|
||||||
|
* Public License V3, which may be found in the FastDFS source kit.
|
||||||
|
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||||
|
**/
|
||||||
|
|
||||||
|
#ifndef SHARED_FUNC_H
|
||||||
|
#define SHARED_FUNC_H
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <sys/resource.h>
|
||||||
|
#include "common_define.h"
|
||||||
|
#include "ini_file_reader.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** lowercase the string
|
||||||
|
* parameters:
|
||||||
|
* src: input string, will be changed
|
||||||
|
* return: lowercased string
|
||||||
|
*/
|
||||||
|
char *toLowercase(char *src);
|
||||||
|
|
||||||
|
/** uppercase the string
|
||||||
|
* parameters:
|
||||||
|
* src: input string, will be changed
|
||||||
|
* return: uppercased string
|
||||||
|
*/
|
||||||
|
char *toUppercase(char *src);
|
||||||
|
|
||||||
|
|
||||||
|
/** date format to string
|
||||||
|
* parameters:
|
||||||
|
* nTime: unix timestamp
|
||||||
|
* szDateFormat: date format, more detail man strftime
|
||||||
|
* buff: store the formated result, can be NULL
|
||||||
|
* buff_size: buffer size, max bytes can contain
|
||||||
|
* return: formated date string
|
||||||
|
*/
|
||||||
|
char *formatDatetime(const time_t nTime, \
|
||||||
|
const char *szDateFormat, \
|
||||||
|
char *buff, const int buff_size);
|
||||||
|
|
||||||
|
/** get character count, only support GB charset
|
||||||
|
* parameters:
|
||||||
|
* s: the string
|
||||||
|
* return: character count
|
||||||
|
*/
|
||||||
|
int getCharLen(const char *s);
|
||||||
|
|
||||||
|
/** replace \r and \n to space
|
||||||
|
* parameters:
|
||||||
|
* s: the string
|
||||||
|
* return: replaced string
|
||||||
|
*/
|
||||||
|
char *replaceCRLF2Space(char *s);
|
||||||
|
|
||||||
|
/** get the filename absolute path
|
||||||
|
* parameters:
|
||||||
|
* fileame: the filename
|
||||||
|
* szAbsPath: store the absolute path
|
||||||
|
* pathSize: max bytes to contain
|
||||||
|
* return: absolute path, NULL for fail
|
||||||
|
*/
|
||||||
|
char *getAbsolutePath(const char *fileame, char *szAbsPath, \
|
||||||
|
const int pathSize);
|
||||||
|
|
||||||
|
/** get the executable file absolute filename
|
||||||
|
* parameters:
|
||||||
|
* exeFilename: the executable filename
|
||||||
|
* szAbsFilename: store the absolute filename
|
||||||
|
* maxSize: max bytes to contain
|
||||||
|
* return: absolute filename, NULL for fail
|
||||||
|
*/
|
||||||
|
char *getExeAbsoluteFilename(const char *exeFilename, char *szAbsFilename, \
|
||||||
|
const int maxSize);
|
||||||
|
|
||||||
|
#ifndef WIN32
|
||||||
|
|
||||||
|
/** get running process count by program name such as fdfs_trackerd
|
||||||
|
* parameters:
|
||||||
|
* progName: the program name
|
||||||
|
* bAllOwners: false for only get my proccess count
|
||||||
|
* return: proccess count, >= 0 success, < 0 fail
|
||||||
|
*/
|
||||||
|
int getProccessCount(const char *progName, const bool bAllOwners);
|
||||||
|
|
||||||
|
/** get running process ids by program name such as fdfs_trackerd
|
||||||
|
* parameters:
|
||||||
|
* progName: the program name
|
||||||
|
* bAllOwners: false for only get my proccess count
|
||||||
|
* pids: store the pids
|
||||||
|
* arrSize: max pids
|
||||||
|
* return: proccess count, >= 0 success, < 0 fail
|
||||||
|
*/
|
||||||
|
int getUserProcIds(const char *progName, const bool bAllOwners, \
|
||||||
|
int pids[], const int arrSize);
|
||||||
|
|
||||||
|
/** execute program, get it's output
|
||||||
|
* parameters:
|
||||||
|
* command: the program
|
||||||
|
* output: store ouput result
|
||||||
|
* buff_size: output max size (bytes)
|
||||||
|
* return: error no, 0 success, != 0 fail
|
||||||
|
*/
|
||||||
|
int getExecResult(const char *command, char *output, const int buff_size);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** daemon init
|
||||||
|
* parameters:
|
||||||
|
* bCloseFiles: if close the stdin, stdout and stderr
|
||||||
|
* return: none
|
||||||
|
*/
|
||||||
|
void daemon_init(bool bCloseFiles);
|
||||||
|
|
||||||
|
/** convert buffer content to hex string such as 0B82A1
|
||||||
|
* parameters:
|
||||||
|
* s: the buffer
|
||||||
|
* len: the buffer length
|
||||||
|
* szHexBuff: store the hex string (must have enough space)
|
||||||
|
* return: hex string (szHexBuff)
|
||||||
|
*/
|
||||||
|
char *bin2hex(const char *s, const int len, char *szHexBuff);
|
||||||
|
|
||||||
|
/** parse hex string to binary content
|
||||||
|
* parameters:
|
||||||
|
* s: the hex string such as 8B04CD
|
||||||
|
* szBinBuff: store the converted binary content(must have enough space)
|
||||||
|
* nDestLen: store the converted content length
|
||||||
|
* return: converted binary content (szBinBuff)
|
||||||
|
*/
|
||||||
|
char *hex2bin(const char *s, char *szBinBuff, int *nDestLen);
|
||||||
|
|
||||||
|
/** print binary buffer as hex string
|
||||||
|
* parameters:
|
||||||
|
* s: the buffer
|
||||||
|
* len: the buffer length
|
||||||
|
* return: none
|
||||||
|
*/
|
||||||
|
void printBuffHex(const char *s, const int len);
|
||||||
|
|
||||||
|
/** 32 bits int convert to buffer (big-endian)
|
||||||
|
* parameters:
|
||||||
|
* n: 32 bits int value
|
||||||
|
* buff: the buffer, at least 4 bytes space, no tail \0
|
||||||
|
* return: none
|
||||||
|
*/
|
||||||
|
void int2buff(const int n, char *buff);
|
||||||
|
|
||||||
|
/** buffer convert to 32 bits int
|
||||||
|
* parameters:
|
||||||
|
* buff: big-endian 4 bytes buffer
|
||||||
|
* return: 32 bits int value
|
||||||
|
*/
|
||||||
|
int buff2int(const char *buff);
|
||||||
|
|
||||||
|
/** long (64 bits) convert to buffer (big-endian)
|
||||||
|
* parameters:
|
||||||
|
* n: 64 bits int value
|
||||||
|
* buff: the buffer, at least 8 bytes space, no tail \0
|
||||||
|
* return: none
|
||||||
|
*/
|
||||||
|
void long2buff(int64_t n, char *buff);
|
||||||
|
|
||||||
|
/** buffer convert to 64 bits int
|
||||||
|
* parameters:
|
||||||
|
* buff: big-endian 8 bytes buffer
|
||||||
|
* return: 64 bits int value
|
||||||
|
*/
|
||||||
|
int64_t buff2long(const char *buff);
|
||||||
|
|
||||||
|
/** trim leading spaces ( \t\r\n)
|
||||||
|
* parameters:
|
||||||
|
* pStr: the string to trim
|
||||||
|
* return: trimed string porinter as pStr
|
||||||
|
*/
|
||||||
|
char *trim_left(char *pStr);
|
||||||
|
|
||||||
|
/** trim tail spaces ( \t\r\n)
|
||||||
|
* parameters:
|
||||||
|
* pStr: the string to trim
|
||||||
|
* return: trimed string porinter as pStr
|
||||||
|
*/
|
||||||
|
char *trim_right(char *pStr);
|
||||||
|
|
||||||
|
/** trim leading and tail spaces ( \t\r\n)
|
||||||
|
* parameters:
|
||||||
|
* pStr: the string to trim
|
||||||
|
* return: trimed string porinter as pStr
|
||||||
|
*/
|
||||||
|
char *trim(char *pStr);
|
||||||
|
|
||||||
|
/** copy string to BufferInfo
|
||||||
|
* parameters:
|
||||||
|
* pBuff: the dest buffer
|
||||||
|
* str: source string
|
||||||
|
* return: error no, 0 success, != 0 fail
|
||||||
|
*/
|
||||||
|
int buffer_strcpy(BufferInfo *pBuff, const char *str);
|
||||||
|
|
||||||
|
/** copy binary buffer to BufferInfo
|
||||||
|
* parameters:
|
||||||
|
* pBuff: the dest buffer
|
||||||
|
* buff: source buffer
|
||||||
|
* len: source buffer length
|
||||||
|
* return: error no, 0 success, != 0 fail
|
||||||
|
*/
|
||||||
|
int buffer_memcpy(BufferInfo *pBuff, const char *buff, const int len);
|
||||||
|
|
||||||
|
/** url encode
|
||||||
|
* parameters:
|
||||||
|
* src: the source string to encode
|
||||||
|
* src_len: source string length
|
||||||
|
* dest: store dest string
|
||||||
|
* dest_len: store the dest string length
|
||||||
|
* return: error no, 0 success, != 0 fail
|
||||||
|
*/
|
||||||
|
char *urlencode(const char *src, const int src_len, char *dest, int *dest_len);
|
||||||
|
|
||||||
|
/** url decode
|
||||||
|
* parameters:
|
||||||
|
* src: the source string to decode
|
||||||
|
* src_len: source string length
|
||||||
|
* dest: store dest string
|
||||||
|
* dest_len: store the dest string length
|
||||||
|
* return: error no, 0 success, != 0 fail
|
||||||
|
*/
|
||||||
|
char *urldecode(const char *src, const int src_len, char *dest, int *dest_len);
|
||||||
|
|
||||||
|
/** get char occurs count
|
||||||
|
* parameters:
|
||||||
|
* src: the source string
|
||||||
|
* seperator: find this char occurs times
|
||||||
|
* return: char occurs count
|
||||||
|
*/
|
||||||
|
int getOccurCount(const char *src, const char seperator);
|
||||||
|
|
||||||
|
/** split string
|
||||||
|
* parameters:
|
||||||
|
* src: the source string, will be modified by this function
|
||||||
|
* seperator: seperator char
|
||||||
|
* nMaxCols: max columns (max split count)
|
||||||
|
* nColCount: store the columns (array elements) count
|
||||||
|
* return: string array, should call freeSplit to free, return NULL when fail
|
||||||
|
*/
|
||||||
|
char **split(char *src, const char seperator, const int nMaxCols, \
|
||||||
|
int *nColCount);
|
||||||
|
|
||||||
|
/** free split results
|
||||||
|
* parameters:
|
||||||
|
* p: return by function split
|
||||||
|
* return: none
|
||||||
|
*/
|
||||||
|
void freeSplit(char **p);
|
||||||
|
|
||||||
|
|
||||||
|
/** split string
|
||||||
|
* parameters:
|
||||||
|
* src: the source string, will be modified by this function
|
||||||
|
* seperator: seperator char
|
||||||
|
* pCols: store split strings
|
||||||
|
* nMaxCols: max columns (max split count)
|
||||||
|
* return: string array / column count
|
||||||
|
*/
|
||||||
|
int splitEx(char *src, const char seperator, char **pCols, const int nMaxCols);
|
||||||
|
|
||||||
|
/** split string
|
||||||
|
* parameters:
|
||||||
|
* src: the source string, will be modified by this function
|
||||||
|
* seperator: seperator char
|
||||||
|
* pCols: store split strings
|
||||||
|
* nMaxCols: max columns (max split count)
|
||||||
|
* return: string array / column count
|
||||||
|
*/
|
||||||
|
int my_strtok(char *src, const char *delim, char **pCols, const int nMaxCols);
|
||||||
|
|
||||||
|
/** check file exist
|
||||||
|
* parameters:
|
||||||
|
* filename: the filename
|
||||||
|
* return: true if file exists, otherwise false
|
||||||
|
*/
|
||||||
|
bool fileExists(const char *filename);
|
||||||
|
|
||||||
|
/** check if a directory
|
||||||
|
* parameters:
|
||||||
|
* filename: the filename
|
||||||
|
* return: true for directory
|
||||||
|
*/
|
||||||
|
bool isDir(const char *filename);
|
||||||
|
|
||||||
|
/** check if a regular file
|
||||||
|
* parameters:
|
||||||
|
* filename: the filename
|
||||||
|
* return: true for regular file
|
||||||
|
*/
|
||||||
|
bool isFile(const char *filename);
|
||||||
|
|
||||||
|
/** check if filename securty, /../ ocur in filename not allowed
|
||||||
|
* parameters:
|
||||||
|
* filename: the filename
|
||||||
|
* len: filename length
|
||||||
|
* return: true for regular file
|
||||||
|
*/
|
||||||
|
bool is_filename_secure(const char *filename, const int len);
|
||||||
|
|
||||||
|
/** load log_level from config context
|
||||||
|
* parameters:
|
||||||
|
* pIniContext: the config context
|
||||||
|
* return: none
|
||||||
|
*/
|
||||||
|
void load_log_level(IniContext *pIniContext);
|
||||||
|
|
||||||
|
/** load log_level from config file
|
||||||
|
* parameters:
|
||||||
|
* conf_filename: the config filename
|
||||||
|
* return: none
|
||||||
|
*/
|
||||||
|
int load_log_level_ex(const char *conf_filename);
|
||||||
|
|
||||||
|
/** set global log level
|
||||||
|
* parameters:
|
||||||
|
* pLogLevel: log level string value
|
||||||
|
* return: none
|
||||||
|
*/
|
||||||
|
void set_log_level(char *pLogLevel);
|
||||||
|
|
||||||
|
/** load allow hosts from config context
|
||||||
|
* parameters:
|
||||||
|
* pIniContext: the config context
|
||||||
|
* allow_ip_addrs: store allow ip addresses
|
||||||
|
* allow_ip_count: store allow ip address count
|
||||||
|
* return: error no , 0 success, != 0 fail
|
||||||
|
*/
|
||||||
|
int load_allow_hosts(IniContext *pIniContext, \
|
||||||
|
in_addr_t **allow_ip_addrs, int *allow_ip_count);
|
||||||
|
|
||||||
|
/** get time item from config context
|
||||||
|
* parameters:
|
||||||
|
* pIniContext: the config context
|
||||||
|
* item_name: item name in config file, time format: hour:minute, such as 15:25
|
||||||
|
* pTimeInfo: store time info
|
||||||
|
* default_hour: default hour value
|
||||||
|
* default_minute: default minute value
|
||||||
|
* return: error no , 0 success, != 0 fail
|
||||||
|
*/
|
||||||
|
int get_time_item_from_conf(IniContext *pIniContext, \
|
||||||
|
const char *item_name, TimeInfo *pTimeInfo, \
|
||||||
|
const byte default_hour, const byte default_minute);
|
||||||
|
|
||||||
|
/** trim path tail char /
|
||||||
|
* parameters:
|
||||||
|
* filePath: the file path to chop
|
||||||
|
* return: none
|
||||||
|
*/
|
||||||
|
void chopPath(char *filePath);
|
||||||
|
|
||||||
|
/** get file content
|
||||||
|
* parameters:
|
||||||
|
* filename: the filename
|
||||||
|
* buff: return the buff, must be freed
|
||||||
|
* file_size: store the file size
|
||||||
|
* return: error no , 0 success, != 0 fail
|
||||||
|
*/
|
||||||
|
int getFileContent(const char *filename, char **buff, int64_t *file_size);
|
||||||
|
|
||||||
|
/** get file content
|
||||||
|
* parameters:
|
||||||
|
* filename: the filename
|
||||||
|
* buff: the buff to store file content
|
||||||
|
* offset: the start offset
|
||||||
|
* size: specify the size to fetch and return the fetched size
|
||||||
|
* return: error no , 0 success, != 0 fail
|
||||||
|
*/
|
||||||
|
int getFileContentEx(const char *filename, char *buff, \
|
||||||
|
int64_t offset, int64_t *size);
|
||||||
|
|
||||||
|
/** write to file
|
||||||
|
* parameters:
|
||||||
|
* filename: the filename to write
|
||||||
|
* buff: the buffer to write
|
||||||
|
* file_size: the file size
|
||||||
|
* return: error no , 0 success, != 0 fail
|
||||||
|
*/
|
||||||
|
int writeToFile(const char *filename, const char *buff, const int file_size);
|
||||||
|
|
||||||
|
/** safe write to file, first write to tmp file, then rename to true filename
|
||||||
|
* parameters:
|
||||||
|
* filename: the filename to write
|
||||||
|
* buff: the buffer to write
|
||||||
|
* file_size: the file size
|
||||||
|
* return: error no , 0 success, != 0 fail
|
||||||
|
*/
|
||||||
|
int safeWriteToFile(const char *filename, const char *buff, \
|
||||||
|
const int file_size);
|
||||||
|
|
||||||
|
/** get a line from file
|
||||||
|
* parameters:
|
||||||
|
* fd: the fd to read
|
||||||
|
* buff: the buffer to store the line
|
||||||
|
* size: the buffer max size
|
||||||
|
* once_bytes: the bytes per read
|
||||||
|
* return: error no , 0 success, != 0 fail
|
||||||
|
*/
|
||||||
|
int fd_gets(int fd, char *buff, const int size, int once_bytes);
|
||||||
|
|
||||||
|
/** set unix rlimit
|
||||||
|
* parameters:
|
||||||
|
* resource: resource id, please see sys/resource.h
|
||||||
|
* value: the value to set
|
||||||
|
* return: error no , 0 success, != 0 fail
|
||||||
|
*/
|
||||||
|
int set_rlimit(int resource, const rlim_t value);
|
||||||
|
|
||||||
|
/** set non block mode
|
||||||
|
* parameters:
|
||||||
|
* fd: the fd to set
|
||||||
|
* adding_flags: the flags to add
|
||||||
|
* return: error no , 0 success, != 0 fail
|
||||||
|
*/
|
||||||
|
int fd_add_flags(int fd, int adding_flags);
|
||||||
|
|
||||||
|
/** set non block mode
|
||||||
|
* parameters:
|
||||||
|
* fd: the fd to set
|
||||||
|
* return: error no , 0 success, != 0 fail
|
||||||
|
*/
|
||||||
|
#define set_nonblock(fd) fd_add_flags(fd, O_NONBLOCK)
|
||||||
|
|
||||||
|
/** set run by group and user
|
||||||
|
* parameters:
|
||||||
|
* group_name: the group name, can be NULL or empty
|
||||||
|
* username: the username, can be NULL or empty
|
||||||
|
* return: error no , 0 success, != 0 fail
|
||||||
|
*/
|
||||||
|
int set_run_by(const char *group_name, const char *username);
|
||||||
|
|
||||||
|
/** compare ip address, type is (in_addr_t *)
|
||||||
|
* parameters:
|
||||||
|
* p1: the first ip address
|
||||||
|
* p2: the second ip address
|
||||||
|
* return: > 0 when p1 > p2, 0 when p1 == p2, < 0 when p1 < p2
|
||||||
|
*/
|
||||||
|
int cmp_by_ip_addr_t(const void *p1, const void *p2);
|
||||||
|
|
||||||
|
/** parse bytes
|
||||||
|
* parameters:
|
||||||
|
* pStr: the string to parse
|
||||||
|
* default_unit_bytes: default unit if not specified the unit like MB etc.
|
||||||
|
* bytes: store the parsed bytes
|
||||||
|
* return: error no , 0 success, != 0 fail
|
||||||
|
*/
|
||||||
|
int parse_bytes(char *pStr, const int default_unit_bytes, int64_t *bytes);
|
||||||
|
|
||||||
|
/** set rand seed
|
||||||
|
* return: error no , 0 success, != 0 fail
|
||||||
|
*/
|
||||||
|
int set_rand_seed();
|
||||||
|
|
||||||
|
/** set timer wrapper
|
||||||
|
* parameters:
|
||||||
|
* first_remain_seconds: remain time for first time, in seconds
|
||||||
|
* interval: the interval
|
||||||
|
* sighandler: handler function
|
||||||
|
* return: error no , 0 success, != 0 fail
|
||||||
|
*/
|
||||||
|
int set_timer(const int first_remain_seconds, const int interval, \
|
||||||
|
void (*sighandler)(int));
|
||||||
|
|
||||||
|
/** set file access and modified times
|
||||||
|
* parameters:
|
||||||
|
* filename: the file to modify times
|
||||||
|
* new_time: the time to set
|
||||||
|
* return: error no , 0 success, != 0 fail
|
||||||
|
*/
|
||||||
|
int set_file_utimes(const char *filename, const time_t new_time);
|
||||||
|
|
||||||
|
/** ignore singal pipe (SIGPIPE)
|
||||||
|
* return: error no , 0 success, != 0 fail
|
||||||
|
*/
|
||||||
|
int ignore_signal_pipe();
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,335 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2008 Happy Fish / YuQing
|
||||||
|
*
|
||||||
|
* FastDFS may be copied only under the terms of the GNU General
|
||||||
|
* Public License V3, which may be found in the FastDFS source kit.
|
||||||
|
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||||
|
**/
|
||||||
|
|
||||||
|
//socketopt.h
|
||||||
|
|
||||||
|
#ifndef _SOCKETOPT_H_
|
||||||
|
#define _SOCKETOPT_H_
|
||||||
|
|
||||||
|
#include "common_define.h"
|
||||||
|
|
||||||
|
#define FDFS_WRITE_BUFF_SIZE 256 * 1024
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef int (*getnamefunc)(int socket, struct sockaddr *address, \
|
||||||
|
socklen_t *address_len);
|
||||||
|
|
||||||
|
typedef int (*tcpsenddatafunc)(int sock, void* data, const int size, \
|
||||||
|
const int timeout);
|
||||||
|
|
||||||
|
typedef int (*tcprecvdata_exfunc)(int sock, void *data, const int size, \
|
||||||
|
const int timeout, int *count);
|
||||||
|
|
||||||
|
#define getSockIpaddr(sock, buff, bufferSize) \
|
||||||
|
getIpaddr(getsockname, sock, buff, bufferSize)
|
||||||
|
|
||||||
|
#define getPeerIpaddr(sock, buff, bufferSize) \
|
||||||
|
getIpaddr(getpeername, sock, buff, bufferSize)
|
||||||
|
|
||||||
|
/** get a line from socket
|
||||||
|
* parameters:
|
||||||
|
* sock: the socket
|
||||||
|
* s: the buffer
|
||||||
|
* size: buffer size (max bytes can receive)
|
||||||
|
* timeout: read timeout
|
||||||
|
* return: error no, 0 success, != 0 fail
|
||||||
|
*/
|
||||||
|
int tcpgets(int sock, char *s, const int size, const int timeout);
|
||||||
|
|
||||||
|
/** recv data (block mode)
|
||||||
|
* parameters:
|
||||||
|
* sock: the socket
|
||||||
|
* data: the buffer
|
||||||
|
* size: buffer size (max bytes can receive)
|
||||||
|
* timeout: read timeout
|
||||||
|
* count: store the bytes recveived
|
||||||
|
* return: error no, 0 success, != 0 fail
|
||||||
|
*/
|
||||||
|
int tcprecvdata_ex(int sock, void *data, const int size, \
|
||||||
|
const int timeout, int *count);
|
||||||
|
|
||||||
|
/** recv data (non-block mode)
|
||||||
|
* parameters:
|
||||||
|
* sock: the socket
|
||||||
|
* data: the buffer
|
||||||
|
* size: buffer size (max bytes can receive)
|
||||||
|
* timeout: read timeout
|
||||||
|
* count: store the bytes recveived
|
||||||
|
* return: error no, 0 success, != 0 fail
|
||||||
|
*/
|
||||||
|
int tcprecvdata_nb_ex(int sock, void *data, const int size, \
|
||||||
|
const int timeout, int *count);
|
||||||
|
|
||||||
|
/** send data (block mode)
|
||||||
|
* parameters:
|
||||||
|
* sock: the socket
|
||||||
|
* data: the buffer to send
|
||||||
|
* size: buffer size
|
||||||
|
* timeout: write timeout
|
||||||
|
* return: error no, 0 success, != 0 fail
|
||||||
|
*/
|
||||||
|
int tcpsenddata(int sock, void* data, const int size, const int timeout);
|
||||||
|
|
||||||
|
/** send data (non-block mode)
|
||||||
|
* parameters:
|
||||||
|
* sock: the socket
|
||||||
|
* data: the buffer to send
|
||||||
|
* size: buffer size
|
||||||
|
* timeout: write timeout
|
||||||
|
* return: error no, 0 success, != 0 fail
|
||||||
|
*/
|
||||||
|
int tcpsenddata_nb(int sock, void* data, const int size, const int timeout);
|
||||||
|
|
||||||
|
/** connect to server by block mode
|
||||||
|
* parameters:
|
||||||
|
* sock: the socket
|
||||||
|
* server_ip: ip address of the server
|
||||||
|
* server_port: port of the server
|
||||||
|
* return: error no, 0 success, != 0 fail
|
||||||
|
*/
|
||||||
|
int connectserverbyip(int sock, const char *server_ip, const short server_port);
|
||||||
|
|
||||||
|
/** connect to server by non-block mode
|
||||||
|
* parameters:
|
||||||
|
* sock: the socket
|
||||||
|
* server_ip: ip address of the server
|
||||||
|
* server_port: port of the server
|
||||||
|
* timeout: connect timeout in seconds
|
||||||
|
* auto_detect: if detect and adjust the block mode of the socket
|
||||||
|
* return: error no, 0 success, != 0 fail
|
||||||
|
*/
|
||||||
|
int connectserverbyip_nb_ex(int sock, const char *server_ip, \
|
||||||
|
const short server_port, const int timeout, \
|
||||||
|
const bool auto_detect);
|
||||||
|
|
||||||
|
/** connect to server by non-block mode, the socket must be set to non-block
|
||||||
|
* parameters:
|
||||||
|
* sock: the socket, must be set to non-block
|
||||||
|
* server_ip: ip address of the server
|
||||||
|
* server_port: port of the server
|
||||||
|
* timeout: connect timeout in seconds
|
||||||
|
* return: error no, 0 success, != 0 fail
|
||||||
|
*/
|
||||||
|
#define connectserverbyip_nb(sock, server_ip, server_port, timeout) \
|
||||||
|
connectserverbyip_nb_ex(sock, server_ip, server_port, timeout, false)
|
||||||
|
|
||||||
|
/** connect to server by non-block mode, auto detect socket block mode
|
||||||
|
* parameters:
|
||||||
|
* sock: the socket, can be block mode
|
||||||
|
* server_ip: ip address of the server
|
||||||
|
* server_port: port of the server
|
||||||
|
* timeout: connect timeout in seconds
|
||||||
|
* return: error no, 0 success, != 0 fail
|
||||||
|
*/
|
||||||
|
#define connectserverbyip_nb_auto(sock, server_ip, server_port, timeout) \
|
||||||
|
connectserverbyip_nb_ex(sock, server_ip, server_port, timeout, true)
|
||||||
|
|
||||||
|
/** accept client connect request
|
||||||
|
* parameters:
|
||||||
|
* sock: the server socket
|
||||||
|
* timeout: read timeout
|
||||||
|
* err_no: store the error no, 0 for success
|
||||||
|
* return: client socket, < 0 for error
|
||||||
|
*/
|
||||||
|
int nbaccept(int sock, const int timeout, int *err_no);
|
||||||
|
|
||||||
|
/** set socket options
|
||||||
|
* parameters:
|
||||||
|
* sock: the socket
|
||||||
|
* timeout: read & write timeout
|
||||||
|
* return: error no, 0 success, != 0 fail
|
||||||
|
*/
|
||||||
|
int tcpsetserveropt(int fd, const int timeout);
|
||||||
|
|
||||||
|
/** set socket non-block options
|
||||||
|
* parameters:
|
||||||
|
* sock: the socket
|
||||||
|
* return: error no, 0 success, != 0 fail
|
||||||
|
*/
|
||||||
|
int tcpsetnonblockopt(int fd);
|
||||||
|
|
||||||
|
/** set socket no delay on send data
|
||||||
|
* parameters:
|
||||||
|
* sock: the socket
|
||||||
|
* timeout: read & write timeout
|
||||||
|
* return: error no, 0 success, != 0 fail
|
||||||
|
*/
|
||||||
|
int tcpsetnodelay(int fd, const int timeout);
|
||||||
|
|
||||||
|
/** set socket keep-alive
|
||||||
|
* parameters:
|
||||||
|
* sock: the socket
|
||||||
|
* idleSeconds: max idle time (seconds)
|
||||||
|
* return: error no, 0 success, != 0 fail
|
||||||
|
*/
|
||||||
|
int tcpsetkeepalive(int fd, const int idleSeconds);
|
||||||
|
|
||||||
|
/** print keep-alive related parameters
|
||||||
|
* parameters:
|
||||||
|
* sock: the socket
|
||||||
|
* return: error no, 0 success, != 0 fail
|
||||||
|
*/
|
||||||
|
int tcpprintkeepalive(int fd);
|
||||||
|
|
||||||
|
/** get ip address
|
||||||
|
* parameters:
|
||||||
|
* getname: the function name, should be getpeername or getsockname
|
||||||
|
* sock: the socket
|
||||||
|
* buff: buffer to store the ip address
|
||||||
|
* bufferSize: the buffer size (max bytes)
|
||||||
|
* return: in_addr_t, INADDR_NONE for fail
|
||||||
|
*/
|
||||||
|
in_addr_t getIpaddr(getnamefunc getname, int sock, \
|
||||||
|
char *buff, const int bufferSize);
|
||||||
|
|
||||||
|
/** get hostname by it's ip address
|
||||||
|
* parameters:
|
||||||
|
* szIpAddr: the ip address
|
||||||
|
* buff: buffer to store the hostname
|
||||||
|
* bufferSize: the buffer size (max bytes)
|
||||||
|
* return: hostname, empty buffer for error
|
||||||
|
*/
|
||||||
|
char *getHostnameByIp(const char *szIpAddr, char *buff, const int bufferSize);
|
||||||
|
|
||||||
|
/** get by ip address by it's hostname
|
||||||
|
* parameters:
|
||||||
|
* name: the hostname
|
||||||
|
* buff: buffer to store the ip address
|
||||||
|
* bufferSize: the buffer size (max bytes)
|
||||||
|
* return: in_addr_t, INADDR_NONE for fail
|
||||||
|
*/
|
||||||
|
in_addr_t getIpaddrByName(const char *name, char *buff, const int bufferSize);
|
||||||
|
|
||||||
|
/** bind wrapper
|
||||||
|
* parameters:
|
||||||
|
* sock: the socket
|
||||||
|
* bind_ipaddr: the ip address to bind
|
||||||
|
* port: the port to bind
|
||||||
|
* return: error no, 0 success, != 0 fail
|
||||||
|
*/
|
||||||
|
int socketBind(int sock, const char *bind_ipaddr, const int port);
|
||||||
|
|
||||||
|
/** start a socket server (socket, bind and listen)
|
||||||
|
* parameters:
|
||||||
|
* sock: the socket
|
||||||
|
* bind_ipaddr: the ip address to bind
|
||||||
|
* port: the port to bind
|
||||||
|
* err_no: store the error no
|
||||||
|
* return: >= 0 server socket, < 0 fail
|
||||||
|
*/
|
||||||
|
int socketServer(const char *bind_ipaddr, const int port, int *err_no);
|
||||||
|
|
||||||
|
#define tcprecvdata(sock, data, size, timeout) \
|
||||||
|
tcprecvdata_ex(sock, data, size, timeout, NULL)
|
||||||
|
|
||||||
|
#define tcpsendfile(sock, filename, file_bytes, timeout, total_send_bytes) \
|
||||||
|
tcpsendfile_ex(sock, filename, 0, file_bytes, timeout, total_send_bytes)
|
||||||
|
|
||||||
|
#define tcprecvdata_nb(sock, data, size, timeout) \
|
||||||
|
tcprecvdata_nb_ex(sock, data, size, timeout, NULL)
|
||||||
|
|
||||||
|
/** send a file
|
||||||
|
* parameters:
|
||||||
|
* sock: the socket
|
||||||
|
* filename: the file to send
|
||||||
|
* file_offset: file offset, start position
|
||||||
|
* file_bytes: send file length
|
||||||
|
* timeout: write timeout
|
||||||
|
* total_send_bytes: store the send bytes
|
||||||
|
* return: error no, 0 success, != 0 fail
|
||||||
|
*/
|
||||||
|
int tcpsendfile_ex(int sock, const char *filename, const int64_t file_offset, \
|
||||||
|
const int64_t file_bytes, const int timeout, int64_t *total_send_bytes);
|
||||||
|
|
||||||
|
/** receive data to a file
|
||||||
|
* parameters:
|
||||||
|
* sock: the socket
|
||||||
|
* filename: the file to write
|
||||||
|
* file_bytes: file size (bytes)
|
||||||
|
* fsync_after_written_bytes: call fsync every x bytes
|
||||||
|
* timeout: read/recv timeout
|
||||||
|
* true_file_bytes: store the true file bytes
|
||||||
|
* return: error no, 0 success, != 0 fail
|
||||||
|
*/
|
||||||
|
int tcprecvfile(int sock, const char *filename, const int64_t file_bytes, \
|
||||||
|
const int fsync_after_written_bytes, const int timeout, \
|
||||||
|
int64_t *true_file_bytes);
|
||||||
|
|
||||||
|
|
||||||
|
#define tcprecvinfinitefile(sock, filename, fsync_after_written_bytes, \
|
||||||
|
timeout, file_bytes) \
|
||||||
|
tcprecvfile(sock, filename, INFINITE_FILE_SIZE, \
|
||||||
|
fsync_after_written_bytes, timeout, file_bytes)
|
||||||
|
|
||||||
|
|
||||||
|
/** receive data to a file
|
||||||
|
* parameters:
|
||||||
|
* sock: the socket
|
||||||
|
* filename: the file to write
|
||||||
|
* file_bytes: file size (bytes)
|
||||||
|
* fsync_after_written_bytes: call fsync every x bytes
|
||||||
|
* hash_codes: return hash code of file content
|
||||||
|
* timeout: read/recv timeout
|
||||||
|
* return: error no, 0 success, != 0 fail
|
||||||
|
*/
|
||||||
|
int tcprecvfile_ex(int sock, const char *filename, const int64_t file_bytes, \
|
||||||
|
const int fsync_after_written_bytes, \
|
||||||
|
unsigned int *hash_codes, const int timeout);
|
||||||
|
|
||||||
|
/** receive specified data and discard
|
||||||
|
* parameters:
|
||||||
|
* sock: the socket
|
||||||
|
* bytes: data bytes to discard
|
||||||
|
* timeout: read timeout
|
||||||
|
* total_recv_bytes: store the total recv bytes
|
||||||
|
* return: error no, 0 success, != 0 fail
|
||||||
|
*/
|
||||||
|
int tcpdiscard(int sock, const int bytes, const int timeout, \
|
||||||
|
int64_t *total_recv_bytes);
|
||||||
|
|
||||||
|
/** get local host ip addresses
|
||||||
|
* parameters:
|
||||||
|
* ip_addrs: store the ip addresses
|
||||||
|
* max_count: max ip address (max ip_addrs elements)
|
||||||
|
* count: store the ip address count
|
||||||
|
* return: error no, 0 success, != 0 fail
|
||||||
|
*/
|
||||||
|
int getlocaladdrs(char ip_addrs[][IP_ADDRESS_SIZE], \
|
||||||
|
const int max_count, int *count);
|
||||||
|
|
||||||
|
/** get local host ip addresses
|
||||||
|
* parameters:
|
||||||
|
* ip_addrs: store the ip addresses
|
||||||
|
* max_count: max ip address (max ip_addrs elements)
|
||||||
|
* count: store the ip address count
|
||||||
|
* return: error no, 0 success, != 0 fail
|
||||||
|
*/
|
||||||
|
int getlocaladdrs1(char ip_addrs[][IP_ADDRESS_SIZE], \
|
||||||
|
const int max_count, int *count);
|
||||||
|
|
||||||
|
/** get local host ip addresses by if alias prefix
|
||||||
|
* parameters:
|
||||||
|
* if_alias_prefixes: if alias prefixes, such as eth, bond etc.
|
||||||
|
* prefix_count: if alias prefix count
|
||||||
|
* ip_addrs: store the ip addresses
|
||||||
|
* max_count: max ip address (max ip_addrs elements)
|
||||||
|
* count: store the ip address count
|
||||||
|
* return: error no, 0 success, != 0 fail
|
||||||
|
*/
|
||||||
|
int gethostaddrs(char **if_alias_prefixes, const int prefix_count, \
|
||||||
|
char ip_addrs[][IP_ADDRESS_SIZE], const int max_count, int *count);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
|
|
@ -0,0 +1,62 @@
|
||||||
|
# connect timeout in seconds
|
||||||
|
# default value is 30s
|
||||||
|
connect_timeout=30
|
||||||
|
|
||||||
|
# network timeout in seconds
|
||||||
|
# default value is 30s
|
||||||
|
network_timeout=60
|
||||||
|
|
||||||
|
# the base path to store log files
|
||||||
|
base_path=/home/yuqing/fastdfs
|
||||||
|
|
||||||
|
# tracker_server can ocur more than once, and tracker_server format is
|
||||||
|
# "host:port", host can be hostname or ip address
|
||||||
|
tracker_server=192.168.0.197:22122
|
||||||
|
|
||||||
|
#standard log level as syslog, case insensitive, value list:
|
||||||
|
### emerg for emergency
|
||||||
|
### alert
|
||||||
|
### crit for critical
|
||||||
|
### error
|
||||||
|
### warn for warning
|
||||||
|
### notice
|
||||||
|
### info
|
||||||
|
### debug
|
||||||
|
log_level=info
|
||||||
|
|
||||||
|
# if use connection pool
|
||||||
|
# default value is false
|
||||||
|
# since V4.05
|
||||||
|
use_connection_pool = false
|
||||||
|
|
||||||
|
# connections whose the idle time exceeds this time will be closed
|
||||||
|
# unit: second
|
||||||
|
# default value is 3600
|
||||||
|
# since V4.05
|
||||||
|
connection_pool_max_idle_time = 3600
|
||||||
|
|
||||||
|
# if load FastDFS parameters from tracker server
|
||||||
|
# since V4.05
|
||||||
|
# default value is false
|
||||||
|
load_fdfs_parameters_from_tracker=false
|
||||||
|
|
||||||
|
# if use storage ID instead of IP address
|
||||||
|
# same as tracker.conf
|
||||||
|
# valid only when load_fdfs_parameters_from_tracker is false
|
||||||
|
# default value is false
|
||||||
|
# since V4.05
|
||||||
|
use_storage_id = false
|
||||||
|
|
||||||
|
# specify storage ids filename, can use relative or absolute path
|
||||||
|
# same as tracker.conf
|
||||||
|
# valid only when load_fdfs_parameters_from_tracker is false
|
||||||
|
# since V4.05
|
||||||
|
storage_ids_filename = storage_ids.conf
|
||||||
|
|
||||||
|
|
||||||
|
#HTTP settings
|
||||||
|
http.tracker_server_port=80
|
||||||
|
|
||||||
|
#use "#include" directive to include HTTP other settiongs
|
||||||
|
##include http.conf
|
||||||
|
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
# HTTP default content type
|
||||||
|
http.default_content_type = application/octet-stream
|
||||||
|
|
||||||
|
# MIME types mapping filename
|
||||||
|
# MIME types file format: MIME_type extensions
|
||||||
|
# such as: image/jpeg jpeg jpg jpe
|
||||||
|
# you can use apache's MIME file: mime.types
|
||||||
|
http.mime_types_filename=mime.types
|
||||||
|
|
||||||
|
# if use token to anti-steal
|
||||||
|
# default value is false (0)
|
||||||
|
http.anti_steal.check_token=false
|
||||||
|
|
||||||
|
# token TTL (time to live), seconds
|
||||||
|
# default value is 600
|
||||||
|
http.anti_steal.token_ttl=900
|
||||||
|
|
||||||
|
# secret key to generate anti-steal token
|
||||||
|
# this parameter must be set when http.anti_steal.check_token set to true
|
||||||
|
# the length of the secret key should not exceed 128 bytes
|
||||||
|
http.anti_steal.secret_key=FastDFS1234567890
|
||||||
|
|
||||||
|
# return the content of the file when check token fail
|
||||||
|
# default value is empty (no file sepecified)
|
||||||
|
http.anti_steal.token_check_fail=/home/yuqing/fastdfs/conf/anti-steal.jpg
|
||||||
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,273 @@
|
||||||
|
# is this config file disabled
|
||||||
|
# false for enabled
|
||||||
|
# true for disabled
|
||||||
|
disabled=false
|
||||||
|
|
||||||
|
# the name of the group this storage server belongs to
|
||||||
|
group_name=group1
|
||||||
|
|
||||||
|
# bind an address of this host
|
||||||
|
# empty for bind all addresses of this host
|
||||||
|
bind_addr=
|
||||||
|
|
||||||
|
# if bind an address of this host when connect to other servers
|
||||||
|
# (this storage server as a client)
|
||||||
|
# true for binding the address configed by above parameter: "bind_addr"
|
||||||
|
# false for binding any address of this host
|
||||||
|
client_bind=true
|
||||||
|
|
||||||
|
# the storage server port
|
||||||
|
port=23000
|
||||||
|
|
||||||
|
# connect timeout in seconds
|
||||||
|
# default value is 30s
|
||||||
|
connect_timeout=30
|
||||||
|
|
||||||
|
# network timeout in seconds
|
||||||
|
# default value is 30s
|
||||||
|
network_timeout=60
|
||||||
|
|
||||||
|
# heart beat interval in seconds
|
||||||
|
heart_beat_interval=30
|
||||||
|
|
||||||
|
# disk usage report interval in seconds
|
||||||
|
stat_report_interval=60
|
||||||
|
|
||||||
|
# the base path to store data and log files
|
||||||
|
base_path=/home/yuqing/fastdfs
|
||||||
|
|
||||||
|
# max concurrent connections the server supported
|
||||||
|
# default value is 256
|
||||||
|
# more max_connections means more memory will be used
|
||||||
|
max_connections=256
|
||||||
|
|
||||||
|
# the buff size to recv / send data
|
||||||
|
# this parameter must more than 8KB
|
||||||
|
# default value is 64KB
|
||||||
|
# since V2.00
|
||||||
|
buff_size = 256KB
|
||||||
|
|
||||||
|
# accept thread count
|
||||||
|
# default value is 1
|
||||||
|
# since V4.07
|
||||||
|
accept_threads=1
|
||||||
|
|
||||||
|
# work thread count, should <= max_connections
|
||||||
|
# work thread deal network io
|
||||||
|
# default value is 4
|
||||||
|
# since V2.00
|
||||||
|
work_threads=4
|
||||||
|
|
||||||
|
# if disk read / write separated
|
||||||
|
## false for mixed read and write
|
||||||
|
## true for separated read and write
|
||||||
|
# default value is true
|
||||||
|
# since V2.00
|
||||||
|
disk_rw_separated = true
|
||||||
|
|
||||||
|
# disk reader thread count per store base path
|
||||||
|
# for mixed read / write, this parameter can be 0
|
||||||
|
# default value is 1
|
||||||
|
# since V2.00
|
||||||
|
disk_reader_threads = 1
|
||||||
|
|
||||||
|
# disk writer thread count per store base path
|
||||||
|
# for mixed read / write, this parameter can be 0
|
||||||
|
# default value is 1
|
||||||
|
# since V2.00
|
||||||
|
disk_writer_threads = 1
|
||||||
|
|
||||||
|
# when no entry to sync, try read binlog again after X milliseconds
|
||||||
|
# must > 0, default value is 200ms
|
||||||
|
sync_wait_msec=50
|
||||||
|
|
||||||
|
# after sync a file, usleep milliseconds
|
||||||
|
# 0 for sync successively (never call usleep)
|
||||||
|
sync_interval=0
|
||||||
|
|
||||||
|
# storage sync start time of a day, time format: Hour:Minute
|
||||||
|
# Hour from 0 to 23, Minute from 0 to 59
|
||||||
|
sync_start_time=00:00
|
||||||
|
|
||||||
|
# storage sync end time of a day, time format: Hour:Minute
|
||||||
|
# Hour from 0 to 23, Minute from 0 to 59
|
||||||
|
sync_end_time=23:59
|
||||||
|
|
||||||
|
# write to the mark file after sync N files
|
||||||
|
# default value is 500
|
||||||
|
write_mark_file_freq=500
|
||||||
|
|
||||||
|
# path(disk or mount point) count, default value is 1
|
||||||
|
store_path_count=1
|
||||||
|
|
||||||
|
# store_path#, based 0, if store_path0 not exists, it's value is base_path
|
||||||
|
# the paths must be exist
|
||||||
|
store_path0=/home/yuqing/fastdfs
|
||||||
|
#store_path1=/home/yuqing/fastdfs2
|
||||||
|
|
||||||
|
# subdir_count * subdir_count directories will be auto created under each
|
||||||
|
# store_path (disk), value can be 1 to 256, default value is 256
|
||||||
|
subdir_count_per_path=256
|
||||||
|
|
||||||
|
# tracker_server can ocur more than once, and tracker_server format is
|
||||||
|
# "host:port", host can be hostname or ip address
|
||||||
|
tracker_server=192.168.209.121:22122
|
||||||
|
|
||||||
|
#standard log level as syslog, case insensitive, value list:
|
||||||
|
### emerg for emergency
|
||||||
|
### alert
|
||||||
|
### crit for critical
|
||||||
|
### error
|
||||||
|
### warn for warning
|
||||||
|
### notice
|
||||||
|
### info
|
||||||
|
### debug
|
||||||
|
log_level=info
|
||||||
|
|
||||||
|
#unix group name to run this program,
|
||||||
|
#not set (empty) means run by the group of current user
|
||||||
|
run_by_group=
|
||||||
|
|
||||||
|
#unix username to run this program,
|
||||||
|
#not set (empty) means run by current user
|
||||||
|
run_by_user=
|
||||||
|
|
||||||
|
# allow_hosts can ocur more than once, host can be hostname or ip address,
|
||||||
|
# "*" means match all ip addresses, can use range like this: 10.0.1.[1-15,20] or
|
||||||
|
# host[01-08,20-25].domain.com, for example:
|
||||||
|
# allow_hosts=10.0.1.[1-15,20]
|
||||||
|
# allow_hosts=host[01-08,20-25].domain.com
|
||||||
|
allow_hosts=*
|
||||||
|
|
||||||
|
# the mode of the files distributed to the data path
|
||||||
|
# 0: round robin(default)
|
||||||
|
# 1: random, distributted by hash code
|
||||||
|
file_distribute_path_mode=0
|
||||||
|
|
||||||
|
# valid when file_distribute_to_path is set to 0 (round robin),
|
||||||
|
# when the written file count reaches this number, then rotate to next path
|
||||||
|
# default value is 100
|
||||||
|
file_distribute_rotate_count=100
|
||||||
|
|
||||||
|
# call fsync to disk when write big file
|
||||||
|
# 0: never call fsync
|
||||||
|
# other: call fsync when written bytes >= this bytes
|
||||||
|
# default value is 0 (never call fsync)
|
||||||
|
fsync_after_written_bytes=0
|
||||||
|
|
||||||
|
# sync log buff to disk every interval seconds
|
||||||
|
# must > 0, default value is 10 seconds
|
||||||
|
sync_log_buff_interval=10
|
||||||
|
|
||||||
|
# sync binlog buff / cache to disk every interval seconds
|
||||||
|
# default value is 60 seconds
|
||||||
|
sync_binlog_buff_interval=10
|
||||||
|
|
||||||
|
# sync storage stat info to disk every interval seconds
|
||||||
|
# default value is 300 seconds
|
||||||
|
sync_stat_file_interval=300
|
||||||
|
|
||||||
|
# thread stack size, should >= 512KB
|
||||||
|
# default value is 512KB
|
||||||
|
thread_stack_size=512KB
|
||||||
|
|
||||||
|
# the priority as a source server for uploading file.
|
||||||
|
# the lower this value, the higher its uploading priority.
|
||||||
|
# default value is 10
|
||||||
|
upload_priority=10
|
||||||
|
|
||||||
|
# the NIC alias prefix, such as eth in Linux, you can see it by ifconfig -a
|
||||||
|
# multi aliases split by comma. empty value means auto set by OS type
|
||||||
|
# default values is empty
|
||||||
|
if_alias_prefix=
|
||||||
|
|
||||||
|
# if check file duplicate, when set to true, use FastDHT to store file indexes
|
||||||
|
# 1 or yes: need check
|
||||||
|
# 0 or no: do not check
|
||||||
|
# default value is 0
|
||||||
|
check_file_duplicate=0
|
||||||
|
|
||||||
|
# file signature method for check file duplicate
|
||||||
|
## hash: four 32 bits hash code
|
||||||
|
## md5: MD5 signature
|
||||||
|
# default value is hash
|
||||||
|
# since V4.01
|
||||||
|
file_signature_method=hash
|
||||||
|
|
||||||
|
# namespace for storing file indexes (key-value pairs)
|
||||||
|
# this item must be set when check_file_duplicate is true / on
|
||||||
|
key_namespace=FastDFS
|
||||||
|
|
||||||
|
# set keep_alive to 1 to enable persistent connection with FastDHT servers
|
||||||
|
# default value is 0 (short connection)
|
||||||
|
keep_alive=0
|
||||||
|
|
||||||
|
# you can use "#include filename" (not include double quotes) directive to
|
||||||
|
# load FastDHT server list, when the filename is a relative path such as
|
||||||
|
# pure filename, the base path is the base path of current/this config file.
|
||||||
|
# must set FastDHT server list when check_file_duplicate is true / on
|
||||||
|
# please see INSTALL of FastDHT for detail
|
||||||
|
##include /home/yuqing/fastdht/conf/fdht_servers.conf
|
||||||
|
|
||||||
|
# if log to access log
|
||||||
|
# default value is false
|
||||||
|
# since V4.00
|
||||||
|
use_access_log = false
|
||||||
|
|
||||||
|
# if rotate the access log every day
|
||||||
|
# default value is false
|
||||||
|
# since V4.00
|
||||||
|
rotate_access_log = false
|
||||||
|
|
||||||
|
# rotate access log time base, time format: Hour:Minute
|
||||||
|
# Hour from 0 to 23, Minute from 0 to 59
|
||||||
|
# default value is 00:00
|
||||||
|
# since V4.00
|
||||||
|
access_log_rotate_time=00:00
|
||||||
|
|
||||||
|
# if rotate the error log every day
|
||||||
|
# default value is false
|
||||||
|
# since V4.02
|
||||||
|
rotate_error_log = false
|
||||||
|
|
||||||
|
# rotate error log time base, time format: Hour:Minute
|
||||||
|
# Hour from 0 to 23, Minute from 0 to 59
|
||||||
|
# default value is 00:00
|
||||||
|
# since V4.02
|
||||||
|
error_log_rotate_time=00:00
|
||||||
|
|
||||||
|
# rotate access log when the log file exceeds this size
|
||||||
|
# 0 means never rotates log file by log file size
|
||||||
|
# default value is 0
|
||||||
|
# since V4.02
|
||||||
|
rotate_access_log_size = 0
|
||||||
|
|
||||||
|
# rotate error log when the log file exceeds this size
|
||||||
|
# 0 means never rotates log file by log file size
|
||||||
|
# default value is 0
|
||||||
|
# since V4.02
|
||||||
|
rotate_error_log_size = 0
|
||||||
|
|
||||||
|
# if skip the invalid record when sync file
|
||||||
|
# default value is false
|
||||||
|
# since V4.02
|
||||||
|
file_sync_skip_invalid_record=false
|
||||||
|
|
||||||
|
# if use connection pool
|
||||||
|
# default value is false
|
||||||
|
# since V4.05
|
||||||
|
use_connection_pool = false
|
||||||
|
|
||||||
|
# connections whose the idle time exceeds this time will be closed
|
||||||
|
# unit: second
|
||||||
|
# default value is 3600
|
||||||
|
# since V4.05
|
||||||
|
connection_pool_max_idle_time = 3600
|
||||||
|
|
||||||
|
# use the ip address of this storage server if domain_name is empty,
|
||||||
|
# else this domain name will ocur in the url redirected by the tracker server
|
||||||
|
http.domain_name=
|
||||||
|
|
||||||
|
# the port of the web server on this storage server
|
||||||
|
http.server_port=8888
|
||||||
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
# <id> <group_name> <ip_or_hostname>
|
||||||
|
# 100001 group1 192.168.0.196
|
||||||
|
# 100002 group1 192.168.0.116
|
||||||
|
|
@ -0,0 +1,260 @@
|
||||||
|
# is this config file disabled
|
||||||
|
# false for enabled
|
||||||
|
# true for disabled
|
||||||
|
disabled=false
|
||||||
|
|
||||||
|
# bind an address of this host
|
||||||
|
# empty for bind all addresses of this host
|
||||||
|
bind_addr=
|
||||||
|
|
||||||
|
# the tracker server port
|
||||||
|
port=22122
|
||||||
|
|
||||||
|
# connect timeout in seconds
|
||||||
|
# default value is 30s
|
||||||
|
connect_timeout=30
|
||||||
|
|
||||||
|
# network timeout in seconds
|
||||||
|
# default value is 30s
|
||||||
|
network_timeout=60
|
||||||
|
|
||||||
|
# the base path to store data and log files
|
||||||
|
base_path=/home/yuqing/fastdfs
|
||||||
|
|
||||||
|
# max concurrent connections this server supported
|
||||||
|
max_connections=256
|
||||||
|
|
||||||
|
# accept thread count
|
||||||
|
# default value is 1
|
||||||
|
# since V4.07
|
||||||
|
accept_threads=1
|
||||||
|
|
||||||
|
# work thread count, should <= max_connections
|
||||||
|
# default value is 4
|
||||||
|
# since V2.00
|
||||||
|
work_threads=4
|
||||||
|
|
||||||
|
# the method of selecting group to upload files
|
||||||
|
# 0: round robin
|
||||||
|
# 1: specify group
|
||||||
|
# 2: load balance, select the max free space group to upload file
|
||||||
|
store_lookup=2
|
||||||
|
|
||||||
|
# which group to upload file
|
||||||
|
# when store_lookup set to 1, must set store_group to the group name
|
||||||
|
store_group=group2
|
||||||
|
|
||||||
|
# which storage server to upload file
|
||||||
|
# 0: round robin (default)
|
||||||
|
# 1: the first server order by ip address
|
||||||
|
# 2: the first server order by priority (the minimal)
|
||||||
|
store_server=0
|
||||||
|
|
||||||
|
# which path(means disk or mount point) of the storage server to upload file
|
||||||
|
# 0: round robin
|
||||||
|
# 2: load balance, select the max free space path to upload file
|
||||||
|
store_path=0
|
||||||
|
|
||||||
|
# which storage server to download file
|
||||||
|
# 0: round robin (default)
|
||||||
|
# 1: the source storage server which the current file uploaded to
|
||||||
|
download_server=0
|
||||||
|
|
||||||
|
# reserved storage space for system or other applications.
|
||||||
|
# if the free(available) space of any stoarge server in
|
||||||
|
# a group <= reserved_storage_space,
|
||||||
|
# no file can be uploaded to this group.
|
||||||
|
# bytes unit can be one of follows:
|
||||||
|
### G or g for gigabyte(GB)
|
||||||
|
### M or m for megabyte(MB)
|
||||||
|
### K or k for kilobyte(KB)
|
||||||
|
### no unit for byte(B)
|
||||||
|
### XX.XX% as ratio such as reserved_storage_space = 10%
|
||||||
|
reserved_storage_space = 10%
|
||||||
|
|
||||||
|
#standard log level as syslog, case insensitive, value list:
|
||||||
|
### emerg for emergency
|
||||||
|
### alert
|
||||||
|
### crit for critical
|
||||||
|
### error
|
||||||
|
### warn for warning
|
||||||
|
### notice
|
||||||
|
### info
|
||||||
|
### debug
|
||||||
|
log_level=info
|
||||||
|
|
||||||
|
#unix group name to run this program,
|
||||||
|
#not set (empty) means run by the group of current user
|
||||||
|
run_by_group=
|
||||||
|
|
||||||
|
#unix username to run this program,
|
||||||
|
#not set (empty) means run by current user
|
||||||
|
run_by_user=
|
||||||
|
|
||||||
|
# allow_hosts can ocur more than once, host can be hostname or ip address,
|
||||||
|
# "*" means match all ip addresses, can use range like this: 10.0.1.[1-15,20] or
|
||||||
|
# host[01-08,20-25].domain.com, for example:
|
||||||
|
# allow_hosts=10.0.1.[1-15,20]
|
||||||
|
# allow_hosts=host[01-08,20-25].domain.com
|
||||||
|
allow_hosts=*
|
||||||
|
|
||||||
|
# sync log buff to disk every interval seconds
|
||||||
|
# default value is 10 seconds
|
||||||
|
sync_log_buff_interval = 10
|
||||||
|
|
||||||
|
# check storage server alive interval seconds
|
||||||
|
check_active_interval = 120
|
||||||
|
|
||||||
|
# thread stack size, should >= 64KB
|
||||||
|
# default value is 64KB
|
||||||
|
thread_stack_size = 64KB
|
||||||
|
|
||||||
|
# auto adjust when the ip address of the storage server changed
|
||||||
|
# default value is true
|
||||||
|
storage_ip_changed_auto_adjust = true
|
||||||
|
|
||||||
|
# storage sync file max delay seconds
|
||||||
|
# default value is 86400 seconds (one day)
|
||||||
|
# since V2.00
|
||||||
|
storage_sync_file_max_delay = 86400
|
||||||
|
|
||||||
|
# the max time of storage sync a file
|
||||||
|
# default value is 300 seconds
|
||||||
|
# since V2.00
|
||||||
|
storage_sync_file_max_time = 300
|
||||||
|
|
||||||
|
# if use a trunk file to store several small files
|
||||||
|
# default value is false
|
||||||
|
# since V3.00
|
||||||
|
use_trunk_file = false
|
||||||
|
|
||||||
|
# the min slot size, should <= 4KB
|
||||||
|
# default value is 256 bytes
|
||||||
|
# since V3.00
|
||||||
|
slot_min_size = 256
|
||||||
|
|
||||||
|
# the max slot size, should > slot_min_size
|
||||||
|
# store the upload file to trunk file when it's size <= this value
|
||||||
|
# default value is 16MB
|
||||||
|
# since V3.00
|
||||||
|
slot_max_size = 16MB
|
||||||
|
|
||||||
|
# the trunk file size, should >= 4MB
|
||||||
|
# default value is 64MB
|
||||||
|
# since V3.00
|
||||||
|
trunk_file_size = 64MB
|
||||||
|
|
||||||
|
# if create trunk file advancely
|
||||||
|
# default value is false
|
||||||
|
# since V3.06
|
||||||
|
trunk_create_file_advance = false
|
||||||
|
|
||||||
|
# the time base to create trunk file
|
||||||
|
# the time format: HH:MM
|
||||||
|
# default value is 02:00
|
||||||
|
# since V3.06
|
||||||
|
trunk_create_file_time_base = 02:00
|
||||||
|
|
||||||
|
# the interval of create trunk file, unit: second
|
||||||
|
# default value is 38400 (one day)
|
||||||
|
# since V3.06
|
||||||
|
trunk_create_file_interval = 86400
|
||||||
|
|
||||||
|
# the threshold to create trunk file
|
||||||
|
# when the free trunk file size less than the threshold, will create
|
||||||
|
# the trunk files
|
||||||
|
# default value is 0
|
||||||
|
# since V3.06
|
||||||
|
trunk_create_file_space_threshold = 20G
|
||||||
|
|
||||||
|
# if check trunk space occupying when loading trunk free spaces
|
||||||
|
# the occupied spaces will be ignored
|
||||||
|
# default value is false
|
||||||
|
# since V3.09
|
||||||
|
# NOTICE: set this parameter to true will slow the loading of trunk spaces
|
||||||
|
# when startup. you should set this parameter to true when neccessary.
|
||||||
|
trunk_init_check_occupying = false
|
||||||
|
|
||||||
|
# if ignore storage_trunk.dat, reload from trunk binlog
|
||||||
|
# default value is false
|
||||||
|
# since V3.10
|
||||||
|
# set to true once for version upgrade when your version less than V3.10
|
||||||
|
trunk_init_reload_from_binlog = false
|
||||||
|
|
||||||
|
# the min interval for compressing the trunk binlog file
|
||||||
|
# unit: second
|
||||||
|
# default value is 0, 0 means never compress
|
||||||
|
# FastDFS compress the trunk binlog when trunk init and trunk destroy
|
||||||
|
# recommand to set this parameter to 86400 (one day)
|
||||||
|
# since V5.01
|
||||||
|
trunk_compress_binlog_min_interval = 0
|
||||||
|
|
||||||
|
# if use storage ID instead of IP address
|
||||||
|
# default value is false
|
||||||
|
# since V4.00
|
||||||
|
use_storage_id = false
|
||||||
|
|
||||||
|
# specify storage ids filename, can use relative or absolute path
|
||||||
|
# since V4.00
|
||||||
|
storage_ids_filename = storage_ids.conf
|
||||||
|
|
||||||
|
# id type of the storage server in the filename, values are:
|
||||||
|
## ip: the ip address of the storage server
|
||||||
|
## id: the server id of the storage server
|
||||||
|
# this paramter is valid only when use_storage_id set to true
|
||||||
|
# default value is ip
|
||||||
|
# since V4.03
|
||||||
|
id_type_in_filename = ip
|
||||||
|
|
||||||
|
# if store slave file use symbol link
|
||||||
|
# default value is false
|
||||||
|
# since V4.01
|
||||||
|
store_slave_file_use_link = false
|
||||||
|
|
||||||
|
# if rotate the error log every day
|
||||||
|
# default value is false
|
||||||
|
# since V4.02
|
||||||
|
rotate_error_log = false
|
||||||
|
|
||||||
|
# rotate error log time base, time format: Hour:Minute
|
||||||
|
# Hour from 0 to 23, Minute from 0 to 59
|
||||||
|
# default value is 00:00
|
||||||
|
# since V4.02
|
||||||
|
error_log_rotate_time=00:00
|
||||||
|
|
||||||
|
# rotate error log when the log file exceeds this size
|
||||||
|
# 0 means never rotates log file by log file size
|
||||||
|
# default value is 0
|
||||||
|
# since V4.02
|
||||||
|
rotate_error_log_size = 0
|
||||||
|
|
||||||
|
# if use connection pool
|
||||||
|
# default value is false
|
||||||
|
# since V4.05
|
||||||
|
use_connection_pool = false
|
||||||
|
|
||||||
|
# connections whose the idle time exceeds this time will be closed
|
||||||
|
# unit: second
|
||||||
|
# default value is 3600
|
||||||
|
# since V4.05
|
||||||
|
connection_pool_max_idle_time = 3600
|
||||||
|
|
||||||
|
# HTTP port on this tracker server
|
||||||
|
http.server_port=8080
|
||||||
|
|
||||||
|
# check storage HTTP server alive interval seconds
|
||||||
|
# <= 0 for never check
|
||||||
|
# default value is 30
|
||||||
|
http.check_alive_interval=30
|
||||||
|
|
||||||
|
# check storage HTTP server alive type, values are:
|
||||||
|
# tcp : connect to the storge server with HTTP port only,
|
||||||
|
# do not request and get response
|
||||||
|
# http: storage check alive url must return http status 200
|
||||||
|
# default value is tcp
|
||||||
|
http.check_alive_type=tcp
|
||||||
|
|
||||||
|
# check storage HTTP server alive uri/url
|
||||||
|
# NOTE: storage embed HTTP server support uri: /status.html
|
||||||
|
http.check_alive_uri=/status.html
|
||||||
|
|
||||||
|
|
@ -0,0 +1,82 @@
|
||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# fdfs_storaged Starts fdfs_storaged
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# chkconfig: 2345 99 01
|
||||||
|
# description: FastDFS storage server
|
||||||
|
### BEGIN INIT INFO
|
||||||
|
# Provides: $fdfs_storaged
|
||||||
|
### END INIT INFO
|
||||||
|
|
||||||
|
# Source function library.
|
||||||
|
. /etc/init.d/functions
|
||||||
|
|
||||||
|
PRG=/usr/local/bin/fdfs_storaged
|
||||||
|
CONF=/etc/fdfs/storage.conf
|
||||||
|
|
||||||
|
if [ ! -f $PRG ]; then
|
||||||
|
echo "file $PRG does not exist!"
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f /usr/local/bin/stop.sh ]; then
|
||||||
|
echo "file /usr/local/bin/stop.sh does not exist!"
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f /usr/local/bin/restart.sh ]; then
|
||||||
|
echo "file /usr/local/bin/restart.sh does not exist!"
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f $CONF ]; then
|
||||||
|
echo "file $CONF does not exist!"
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
CMD="$PRG $CONF"
|
||||||
|
RETVAL=0
|
||||||
|
|
||||||
|
start() {
|
||||||
|
echo -n $"Starting FastDFS storage server: "
|
||||||
|
$CMD &
|
||||||
|
RETVAL=$?
|
||||||
|
echo
|
||||||
|
return $RETVAL
|
||||||
|
}
|
||||||
|
stop() {
|
||||||
|
/usr/local/bin/stop.sh $CMD
|
||||||
|
RETVAL=$?
|
||||||
|
return $RETVAL
|
||||||
|
}
|
||||||
|
rhstatus() {
|
||||||
|
status fdfs_storaged
|
||||||
|
}
|
||||||
|
restart() {
|
||||||
|
/usr/local/bin/restart.sh $CMD &
|
||||||
|
}
|
||||||
|
|
||||||
|
case "$1" in
|
||||||
|
start)
|
||||||
|
start
|
||||||
|
;;
|
||||||
|
stop)
|
||||||
|
stop
|
||||||
|
;;
|
||||||
|
status)
|
||||||
|
rhstatus
|
||||||
|
;;
|
||||||
|
restart|reload)
|
||||||
|
restart
|
||||||
|
;;
|
||||||
|
condrestart)
|
||||||
|
restart
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo $"Usage: $0 {start|stop|status|restart|condrestart}"
|
||||||
|
exit 1
|
||||||
|
esac
|
||||||
|
|
||||||
|
exit $?
|
||||||
|
|
||||||
|
|
@ -0,0 +1,82 @@
|
||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# fdfs_trackerd Starts fdfs_trackerd
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# chkconfig: 2345 99 01
|
||||||
|
# description: FastDFS tracker server
|
||||||
|
### BEGIN INIT INFO
|
||||||
|
# Provides: $fdfs_trackerd
|
||||||
|
### END INIT INFO
|
||||||
|
|
||||||
|
# Source function library.
|
||||||
|
. /etc/init.d/functions
|
||||||
|
|
||||||
|
PRG=/usr/local/bin/fdfs_trackerd
|
||||||
|
CONF=/etc/fdfs/tracker.conf
|
||||||
|
|
||||||
|
if [ ! -f $PRG ]; then
|
||||||
|
echo "file $PRG does not exist!"
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f /usr/local/bin/stop.sh ]; then
|
||||||
|
echo "file /usr/local/bin/stop.sh does not exist!"
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f /usr/local/bin/restart.sh ]; then
|
||||||
|
echo "file /usr/local/bin/restart.sh does not exist!"
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f $CONF ]; then
|
||||||
|
echo "file $CONF does not exist!"
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
CMD="$PRG $CONF"
|
||||||
|
RETVAL=0
|
||||||
|
|
||||||
|
start() {
|
||||||
|
echo -n $"Starting FastDFS tracker server: "
|
||||||
|
$CMD &
|
||||||
|
RETVAL=$?
|
||||||
|
echo
|
||||||
|
return $RETVAL
|
||||||
|
}
|
||||||
|
stop() {
|
||||||
|
/usr/local/bin/stop.sh $CMD
|
||||||
|
RETVAL=$?
|
||||||
|
return $RETVAL
|
||||||
|
}
|
||||||
|
rhstatus() {
|
||||||
|
status fdfs_trackerd
|
||||||
|
}
|
||||||
|
restart() {
|
||||||
|
/usr/local/bin/restart.sh $CMD &
|
||||||
|
}
|
||||||
|
|
||||||
|
case "$1" in
|
||||||
|
start)
|
||||||
|
start
|
||||||
|
;;
|
||||||
|
stop)
|
||||||
|
stop
|
||||||
|
;;
|
||||||
|
status)
|
||||||
|
rhstatus
|
||||||
|
;;
|
||||||
|
restart|reload)
|
||||||
|
restart
|
||||||
|
;;
|
||||||
|
condrestart)
|
||||||
|
restart
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo $"Usage: $0 {start|stop|status|restart|condrestart}"
|
||||||
|
exit 1
|
||||||
|
esac
|
||||||
|
|
||||||
|
exit $?
|
||||||
|
|
||||||
|
|
@ -0,0 +1,208 @@
|
||||||
|
tmp_src_filename=fdfs_check_bits.c
|
||||||
|
cat <<EOF > $tmp_src_filename
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
printf("%d\n", (int)sizeof(long));
|
||||||
|
printf("%d\n", (int)sizeof(off_t));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
gcc -D_FILE_OFFSET_BITS=64 -o a.out $tmp_src_filename
|
||||||
|
output=$(./a.out)
|
||||||
|
|
||||||
|
if [ -f /bin/expr ]; then
|
||||||
|
EXPR=/bin/expr
|
||||||
|
else
|
||||||
|
EXPR=/usr/bin/expr
|
||||||
|
fi
|
||||||
|
|
||||||
|
count=0
|
||||||
|
int_bytes=4
|
||||||
|
off_bytes=8
|
||||||
|
for col in $output; do
|
||||||
|
if [ $count -eq 0 ]; then
|
||||||
|
int_bytes=$col
|
||||||
|
else
|
||||||
|
off_bytes=$col
|
||||||
|
fi
|
||||||
|
|
||||||
|
count=$($EXPR $count + 1)
|
||||||
|
done
|
||||||
|
|
||||||
|
/bin/rm -f a.out $tmp_src_filename
|
||||||
|
if [ "$int_bytes" -eq 8 ]; then
|
||||||
|
OS_BITS=64
|
||||||
|
else
|
||||||
|
OS_BITS=32
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$off_bytes" -eq 8 ]; then
|
||||||
|
OFF_BITS=64
|
||||||
|
else
|
||||||
|
OFF_BITS=32
|
||||||
|
fi
|
||||||
|
|
||||||
|
cat <<EOF > common/_os_bits.h
|
||||||
|
#ifndef _OS_BITS_H
|
||||||
|
#define _OS_BITS_H
|
||||||
|
|
||||||
|
#define OS_BITS $OS_BITS
|
||||||
|
#define OFF_BITS $OFF_BITS
|
||||||
|
|
||||||
|
#endif
|
||||||
|
EOF
|
||||||
|
|
||||||
|
ENABLE_STATIC_LIB=0
|
||||||
|
ENABLE_SHARED_LIB=1
|
||||||
|
TARGET_PREFIX=/usr/local
|
||||||
|
TARGET_CONF_PATH=/etc/fdfs
|
||||||
|
|
||||||
|
#WITH_LINUX_SERVICE=1
|
||||||
|
|
||||||
|
DEBUG_FLAG=1
|
||||||
|
|
||||||
|
CFLAGS='-Wall -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE'
|
||||||
|
if [ "$DEBUG_FLAG" = "1" ]; then
|
||||||
|
CFLAGS="$CFLAGS -g -O -DDEBUG_FLAG"
|
||||||
|
else
|
||||||
|
CFLAGS="$CFLAGS -O3"
|
||||||
|
fi
|
||||||
|
|
||||||
|
LIBS=''
|
||||||
|
uname=$(uname)
|
||||||
|
if [ "$uname" = "Linux" ]; then
|
||||||
|
CFLAGS="$CFLAGS -DOS_LINUX -DIOEVENT_USE_EPOLL"
|
||||||
|
elif [ "$uname" = "FreeBSD" ]; then
|
||||||
|
CFLAGS="$CFLAGS -DOS_FREEBSD -DIOEVENT_USE_KQUEUE"
|
||||||
|
elif [ "$uname" = "SunOS" ]; then
|
||||||
|
CFLAGS="$CFLAGS -DOS_SUNOS -D_THREAD_SAFE -DIOEVENT_USE_PORT"
|
||||||
|
LIBS="$LIBS -lsocket -lnsl -lresolv"
|
||||||
|
export CC=gcc
|
||||||
|
elif [ "$uname" = "AIX" ]; then
|
||||||
|
CFLAGS="$CFLAGS -DOS_AIX -D_THREAD_SAFE"
|
||||||
|
export CC=gcc
|
||||||
|
elif [ "$uname" = "HP-UX" ]; then
|
||||||
|
CFLAGS="$CFLAGS -DOS_HPUX"
|
||||||
|
fi
|
||||||
|
|
||||||
|
have_pthread=0
|
||||||
|
if [ -f /usr/lib/libpthread.so ] || [ -f /usr/local/lib/libpthread.so ] || [ -f /lib64/libpthread.so ] || [ -f /usr/lib64/libpthread.so ] || [ -f /usr/lib/libpthread.a ] || [ -f /usr/local/lib/libpthread.a ] || [ -f /lib64/libpthread.a ] || [ -f /usr/lib64/libpthread.a ]; then
|
||||||
|
LIBS="$LIBS -lpthread"
|
||||||
|
have_pthread=1
|
||||||
|
elif [ "$uname" = "HP-UX" ]; then
|
||||||
|
lib_path="/usr/lib/hpux$OS_BITS"
|
||||||
|
if [ -f $lib_path/libpthread.so ]; then
|
||||||
|
LIBS="-L$lib_path -lpthread"
|
||||||
|
have_pthread=1
|
||||||
|
fi
|
||||||
|
elif [ "$uname" = "FreeBSD" ]; then
|
||||||
|
if [ -f /usr/lib/libc_r.so ]; then
|
||||||
|
line=$(nm -D /usr/lib/libc_r.so | grep pthread_create | grep -w T)
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
LIBS="$LIBS -lc_r"
|
||||||
|
have_pthread=1
|
||||||
|
fi
|
||||||
|
elif [ -f /lib64/libc_r.so ]; then
|
||||||
|
line=$(nm -D /lib64/libc_r.so | grep pthread_create | grep -w T)
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
LIBS="$LIBS -lc_r"
|
||||||
|
have_pthread=1
|
||||||
|
fi
|
||||||
|
elif [ -f /usr/lib64/libc_r.so ]; then
|
||||||
|
line=$(nm -D /usr/lib64/libc_r.so | grep pthread_create | grep -w T)
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
LIBS="$LIBS -lc_r"
|
||||||
|
have_pthread=1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $have_pthread -eq 0 ]; then
|
||||||
|
/sbin/ldconfig -p | fgrep libpthread.so > /dev/null
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
LIBS="$LIBS -lpthread"
|
||||||
|
else
|
||||||
|
echo -E 'Require pthread lib, please check!'
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
TRACKER_EXTRA_OBJS=''
|
||||||
|
STORAGE_EXTRA_OBJS=''
|
||||||
|
if [ "$DEBUG_FLAG" = "1" ]; then
|
||||||
|
TRACKER_EXTRA_OBJS="$TRACKER_EXTRA_OBJS tracker_dump.o"
|
||||||
|
STORAGE_EXTRA_OBJS="$STORAGE_EXTRA_OBJS storage_dump.o"
|
||||||
|
|
||||||
|
if [ "$uname" = "Linux" ]; then
|
||||||
|
LIBS="$LIBS -ldl -rdynamic"
|
||||||
|
TRACKER_EXTRA_OBJS="$TRACKER_EXTRA_OBJS ../common/linux_stack_trace.o"
|
||||||
|
STORAGE_EXTRA_OBJS="$STORAGE_EXTRA_OBJS ../common/linux_stack_trace.o"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd tracker
|
||||||
|
cp Makefile.in Makefile
|
||||||
|
perl -pi -e "s#\\\$\(CFLAGS\)#$CFLAGS#g" Makefile
|
||||||
|
perl -pi -e "s#\\\$\(LIBS\)#$LIBS#g" Makefile
|
||||||
|
perl -pi -e "s#\\\$\(TARGET_PREFIX\)#$TARGET_PREFIX#g" Makefile
|
||||||
|
perl -pi -e "s#\\\$\(TRACKER_EXTRA_OBJS\)#$TRACKER_EXTRA_OBJS#g" Makefile
|
||||||
|
perl -pi -e "s#\\\$\(TARGET_CONF_PATH\)#$TARGET_CONF_PATH#g" Makefile
|
||||||
|
make $1 $2
|
||||||
|
|
||||||
|
cd ../storage
|
||||||
|
cp Makefile.in Makefile
|
||||||
|
perl -pi -e "s#\\\$\(CFLAGS\)#$CFLAGS#g" Makefile
|
||||||
|
perl -pi -e "s#\\\$\(LIBS\)#$LIBS#g" Makefile
|
||||||
|
perl -pi -e "s#\\\$\(TARGET_PREFIX\)#$TARGET_PREFIX#g" Makefile
|
||||||
|
perl -pi -e "s#\\\$\(STORAGE_EXTRA_OBJS\)#$STORAGE_EXTRA_OBJS#g" Makefile
|
||||||
|
perl -pi -e "s#\\\$\(TARGET_CONF_PATH\)#$TARGET_CONF_PATH#g" Makefile
|
||||||
|
make $1 $2
|
||||||
|
|
||||||
|
cd ../client
|
||||||
|
cp Makefile.in Makefile
|
||||||
|
perl -pi -e "s#\\\$\(CFLAGS\)#$CFLAGS#g" Makefile
|
||||||
|
perl -pi -e "s#\\\$\(LIBS\)#$LIBS#g" Makefile
|
||||||
|
perl -pi -e "s#\\\$\(TARGET_PREFIX\)#$TARGET_PREFIX#g" Makefile
|
||||||
|
perl -pi -e "s#\\\$\(TARGET_CONF_PATH\)#$TARGET_CONF_PATH#g" Makefile
|
||||||
|
perl -pi -e "s#\\\$\(ENABLE_STATIC_LIB\)#$ENABLE_STATIC_LIB#g" Makefile
|
||||||
|
perl -pi -e "s#\\\$\(ENABLE_SHARED_LIB\)#$ENABLE_SHARED_LIB#g" Makefile
|
||||||
|
|
||||||
|
cp fdfs_link_library.sh.in fdfs_link_library.sh
|
||||||
|
perl -pi -e "s#\\\$\(TARGET_PREFIX\)#$TARGET_PREFIX#g" fdfs_link_library.sh
|
||||||
|
make $1 $2
|
||||||
|
|
||||||
|
cd test
|
||||||
|
cp Makefile.in Makefile
|
||||||
|
perl -pi -e "s#\\\$\(CFLAGS\)#$CFLAGS#g" Makefile
|
||||||
|
perl -pi -e "s#\\\$\(LIBS\)#$LIBS#g" Makefile
|
||||||
|
perl -pi -e "s#\\\$\(TARGET_PREFIX\)#$TARGET_PREFIX#g" Makefile
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
if [ "$1" = "install" ]; then
|
||||||
|
cd ..
|
||||||
|
cp -f restart.sh $TARGET_PREFIX/bin
|
||||||
|
cp -f stop.sh $TARGET_PREFIX/bin
|
||||||
|
|
||||||
|
if [ "$uname" = "Linux" ]; then
|
||||||
|
if [ "$WITH_LINUX_SERVICE" = "1" ]; then
|
||||||
|
if [ ! -d /etc/fdfs ]; then
|
||||||
|
mkdir -p /etc/fdfs
|
||||||
|
cp -f conf/tracker.conf /etc/fdfs/
|
||||||
|
cp -f conf/storage.conf /etc/fdfs/
|
||||||
|
cp -f conf/client.conf /etc/fdfs/
|
||||||
|
cp -f conf/http.conf /etc/fdfs/
|
||||||
|
cp -f conf/mime.types /etc/fdfs/
|
||||||
|
fi
|
||||||
|
|
||||||
|
cp -f init.d/fdfs_trackerd /etc/rc.d/init.d/
|
||||||
|
cp -f init.d/fdfs_storaged /etc/rc.d/init.d/
|
||||||
|
/sbin/chkconfig --add fdfs_trackerd
|
||||||
|
/sbin/chkconfig --add fdfs_storaged
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,22 @@
|
||||||
|
dnl config.m4 for extension fastdfs_client
|
||||||
|
|
||||||
|
PHP_ARG_WITH(fastdfs_client, for fastdfs_client support FastDFS client,
|
||||||
|
[ --with-fastdfs_client Include fastdfs_client support FastDFS client])
|
||||||
|
|
||||||
|
if test "$PHP_FASTDFS_CLIENT" != "no"; then
|
||||||
|
PHP_SUBST(FASTDFS_CLIENT_SHARED_LIBADD)
|
||||||
|
|
||||||
|
if test -z "$ROOT"; then
|
||||||
|
ROOT=/usr/local
|
||||||
|
fi
|
||||||
|
|
||||||
|
PHP_ADD_INCLUDE($ROOT/include/fastcommon)
|
||||||
|
PHP_ADD_INCLUDE($ROOT/include/fastdfs)
|
||||||
|
|
||||||
|
PHP_ADD_LIBRARY_WITH_PATH(fastcommon, $ROOT/lib, FASTDFS_CLIENT_SHARED_LIBADD)
|
||||||
|
PHP_ADD_LIBRARY_WITH_PATH(fdfsclient, $ROOT/lib, FASTDFS_CLIENT_SHARED_LIBADD)
|
||||||
|
|
||||||
|
PHP_NEW_EXTENSION(fastdfs_client, fastdfs_client.c, $ext_shared)
|
||||||
|
|
||||||
|
CFLAGS="$CFLAGS -Wall"
|
||||||
|
fi
|
||||||
|
|
@ -0,0 +1,88 @@
|
||||||
|
<?php
|
||||||
|
echo fastdfs_client_version() . "\n";
|
||||||
|
|
||||||
|
$file_info = fastdfs_storage_upload_appender_by_filename("/usr/include/stdio.h");
|
||||||
|
if (!$file_info)
|
||||||
|
{
|
||||||
|
echo "fastdfs_storage_upload_appender_by_filename fail, errno: " . fastdfs_get_last_error_no() . ", error info: " . fastdfs_get_last_error_info() . "\n";
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$group_name = $file_info['group_name'];
|
||||||
|
$remote_filename = $file_info['filename'];
|
||||||
|
|
||||||
|
var_dump($file_info);
|
||||||
|
$file_id = "$group_name/$remote_filename";
|
||||||
|
var_dump(fastdfs_get_file_info($group_name, $remote_filename));
|
||||||
|
|
||||||
|
$appender_filename = $remote_filename;
|
||||||
|
echo "file id: $group_name/$appender_filename\n";
|
||||||
|
if (!fastdfs_storage_append_by_filename("/usr/include/stdlib.h", $group_name, $appender_filename))
|
||||||
|
{
|
||||||
|
echo "fastdfs_storage_append_by_filename fail, errno: " . fastdfs_get_last_error_no() . ", error info: " . fastdfs_get_last_error_info() . "\n";
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
var_dump(fastdfs_get_file_info($group_name, $appender_filename));
|
||||||
|
|
||||||
|
if (!fastdfs_storage_modify_by_filename("/usr/include/stdlib.h", 0, $group_name, $appender_filename))
|
||||||
|
{
|
||||||
|
echo "fastdfs_storage_modify_by_filename fail, errno: " . fastdfs_get_last_error_no() . ", error info: " . fastdfs_get_last_error_info() . "\n";
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
var_dump(fastdfs_get_file_info($group_name, $appender_filename));
|
||||||
|
|
||||||
|
if (!fastdfs_storage_truncate_file($group_name, $appender_filename, 0))
|
||||||
|
{
|
||||||
|
echo "fastdfs_storage_truncate_file fail, errno: " . fastdfs_get_last_error_no() . ", error info: " . fastdfs_get_last_error_info() . "\n";
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
var_dump(fastdfs_get_file_info($group_name, $appender_filename));
|
||||||
|
|
||||||
|
echo "function test done\n\n";
|
||||||
|
|
||||||
|
$fdfs = new FastDFS();
|
||||||
|
$file_info = $fdfs->storage_upload_appender_by_filename("/usr/include/stdio.h");
|
||||||
|
if (!$file_info)
|
||||||
|
{
|
||||||
|
echo "$fdfs->storage_upload_appender_by_filename fail, errno: " . $fdfs->get_last_error_no() . ", error info: " . $fdfs->get_last_error_info() . "\n";
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$group_name = $file_info['group_name'];
|
||||||
|
$remote_filename = $file_info['filename'];
|
||||||
|
|
||||||
|
var_dump($file_info);
|
||||||
|
$file_id = "$group_name/$remote_filename";
|
||||||
|
var_dump($fdfs->get_file_info($group_name, $remote_filename));
|
||||||
|
|
||||||
|
$appender_filename = $remote_filename;
|
||||||
|
echo "file id: $group_name/$appender_filename\n";
|
||||||
|
if (!$fdfs->storage_append_by_filename("/usr/include/stdlib.h", $group_name, $appender_filename))
|
||||||
|
{
|
||||||
|
echo "$fdfs->storage_append_by_filename fail, errno: " . $fdfs->get_last_error_no() . ", error info: " . $fdfs->get_last_error_info() . "\n";
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
var_dump($fdfs->get_file_info($group_name, $appender_filename));
|
||||||
|
|
||||||
|
if (!$fdfs->storage_modify_by_filename("/usr/include/stdlib.h", 0, $group_name, $appender_filename))
|
||||||
|
{
|
||||||
|
echo "$fdfs->storage_modify_by_filename fail, errno: " . $fdfs->get_last_error_no() . ", error info: " . $fdfs->get_last_error_info() . "\n";
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
var_dump($fdfs->get_file_info($group_name, $appender_filename));
|
||||||
|
|
||||||
|
if (!$fdfs->storage_truncate_file($group_name, $appender_filename))
|
||||||
|
{
|
||||||
|
echo "$fdfs->storage_truncate_file fail, errno: " . $fdfs->get_last_error_no() . ", error info: " . $fdfs->get_last_error_info() . "\n";
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
var_dump($fdfs->get_file_info($group_name, $appender_filename));
|
||||||
|
|
||||||
|
echo 'tracker_close_all_connections result: ' . $fdfs->tracker_close_all_connections() . "\n";
|
||||||
|
?>
|
||||||
|
|
@ -0,0 +1,76 @@
|
||||||
|
<?php
|
||||||
|
echo fastdfs_client_version() . "\n";
|
||||||
|
|
||||||
|
|
||||||
|
$appender_file_id = fastdfs_storage_upload_appender_by_filename1("/usr/include/stdio.h");
|
||||||
|
if (!$appender_file_id)
|
||||||
|
{
|
||||||
|
echo "fastdfs_storage_upload_appender_by_filename1 fail, errno: " . fastdfs_get_last_error_no() . ", error info: " . fastdfs_get_last_error_info() . "\n";
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
var_dump($appender_file_id);
|
||||||
|
var_dump(fastdfs_get_file_info1($appender_file_id));
|
||||||
|
|
||||||
|
if (!fastdfs_storage_append_by_filename1("/usr/include/stdlib.h", $appender_file_id))
|
||||||
|
{
|
||||||
|
echo "fastdfs_storage_append_by_filename1 fail, errno: " . fastdfs_get_last_error_no() . ", error info: " . fastdfs_get_last_error_info() . "\n";
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
var_dump(fastdfs_get_file_info1($appender_file_id));
|
||||||
|
|
||||||
|
if (!fastdfs_storage_modify_by_filename1("/usr/include/stdlib.h", 0, $appender_file_id))
|
||||||
|
{
|
||||||
|
echo "fastdfs_storage_modify_by_filename1 fail, errno: " . fastdfs_get_last_error_no() . ", error info: " . fastdfs_get_last_error_info() . "\n";
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
var_dump(fastdfs_get_file_info1($appender_file_id));
|
||||||
|
|
||||||
|
if (!fastdfs_storage_truncate_file1($appender_file_id, 0))
|
||||||
|
{
|
||||||
|
echo "fastdfs_storage_truncate_file1 fail, errno: " . fastdfs_get_last_error_no() . ", error info: " . fastdfs_get_last_error_info() . "\n";
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
var_dump(fastdfs_get_file_info1($appender_file_id));
|
||||||
|
echo "function test done\n\n";
|
||||||
|
|
||||||
|
$fdfs = new FastDFS();
|
||||||
|
$appender_file_id = $fdfs->storage_upload_appender_by_filename1("/usr/include/stdio.h");
|
||||||
|
if (!$appender_file_id)
|
||||||
|
{
|
||||||
|
echo "\$fdfs->storage_upload_appender_by_filename1 fail, errno: " . $fdfs->get_last_error_no() . ", error info: " . $fdfs->get_last_error_info() . "\n";
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
var_dump($appender_file_id);
|
||||||
|
var_dump($fdfs->get_file_info1($appender_file_id));
|
||||||
|
|
||||||
|
if (!$fdfs->storage_append_by_filename1("/usr/include/stdlib.h", $appender_file_id))
|
||||||
|
{
|
||||||
|
echo "\$fdfs->storage_append_by_filename1 fail, errno: " . $fdfs->get_last_error_no() . ", error info: " . $fdfs->get_last_error_info() . "\n";
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
var_dump($fdfs->get_file_info1($appender_file_id));
|
||||||
|
|
||||||
|
if (!$fdfs->storage_modify_by_filename1("/usr/include/stdlib.h", 0, $appender_file_id))
|
||||||
|
{
|
||||||
|
echo "\$fdfs->storage_modify_by_filename1 fail, errno: " . $fdfs->get_last_error_no() . ", error info: " . $fdfs->get_last_error_info() . "\n";
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
var_dump($fdfs->get_file_info1($appender_file_id));
|
||||||
|
|
||||||
|
if (!$fdfs->storage_truncate_file1($appender_file_id))
|
||||||
|
{
|
||||||
|
echo "\$fdfs->torage_truncate_file1 torage_modify_by_filename1 fail, errno: " . $fdfs->get_last_error_no() . ", error info: " . $fdfs->get_last_error_info() . "\n";
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
var_dump($fdfs->get_file_info1($appender_file_id));
|
||||||
|
|
||||||
|
echo 'tracker_close_all_connections result: ' . $fdfs->tracker_close_all_connections() . "\n";
|
||||||
|
?>
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue