pax_global_header00006660000000000000000000000064113335114720014512gustar00rootroot0000000000000052 comment=d9caf7c0ba1d353f638ae88eace657d4cbf8157e tokyotyrant-1.1.40/000077500000000000000000000000001133351147200142045ustar00rootroot00000000000000tokyotyrant-1.1.40/.pc/000077500000000000000000000000001133351147200146645ustar00rootroot00000000000000tokyotyrant-1.1.40/.pc/.version000066400000000000000000000000021133351147200163420ustar00rootroot000000000000002 tokyotyrant-1.1.40/COPYING000066400000000000000000000634761133351147200152570ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser 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 Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "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 LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY 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 LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! tokyotyrant-1.1.40/ChangeLog000066400000000000000000000351101133351147200157560ustar00rootroot000000000000002010-01-01 Mikio Hirabayashi * ttserver.c (proc): a bug related to initialization of the skeleton was fixed. * tculog.c (tculogadbaddint, tculogadbadddouble): efficiency of zero was improved. * tcrmgr.c (runput, procput): addint and adddouble were added. - Release: 1.1.40 2009-12-07 Mikio Hirabayashi * ttutil.c (ttservstart): error handler for the accept call was added. * ttserver.c (proc): the system connection limit is now reset. - Release: 1.1.39 2009-11-05 Mikio Hirabayashi * ttserver.c (do_mc_append, do_mc_prepend): new functions. - Release: 1.1.38 2009-10-20 Mikio Hirabayashi * ttserver.c (do_stat): "fatal" attribute was added. * ttutil.c (ttwaitsock): new function. * ttutil.c (ttopensock, ttsocksend, ttsockgetc): "errno" is now copied just in case. * tcrdb.c (tcrdbparasearchworker): a bug of race condition was fixed. - Release: 1.1.37 2009-10-06 Mikio Hirabayashi * scrext.c (serv_strstr): new function. * ttserver.c (main, proc): "-mul" option was added. - Release: 1.1.36 2009-09-18 Mikio Hirabayashi * tculog.c (tculogadbputshl, tculogadbputshlproc): new functions. * scrext.c (iterrec): a bug related to handling the return value was fixed. * ttserver.c (do_http_post): "tcadbmisc" is now supported. * ttserver.c (do_stat, do_mc_stats): counts of command execution are now output. * tcrdb.c (tcrdbparasearch, tcrdbparasearchworker): new functions. - Release: 1.1.35 2009-08-04 Mikio Hirabayashi * tcrdb.c (tcrdbput, tcrdbout, tcrdbget): reconnect mechanism was enhanced. * tcrdb.c (tcrdbreconnect): delay mecahism was added. * tculog.c (tculrdnew): a bug related to timestamp detection was fixed. * tcrmgr.c (runimporttsv, procimporttsv): "-sep" option was added. * ttserver.c (do_log): a bug of buffer overrun was fixed. - Release: 1.1.34 2009-07-18 Mikio Hirabayashi * scrext.c (serv_bit): new function. * tcrdb.c (tcrdbqryhint, tcrdbmetasearch): new functions. - Release: 1.1.33 2009-07-15 Mikio Hirabayashi * ttserver.c (do_mget, do_fwmkeys, do_misc): alignment violations were cleared. * tcrmttest.c (runtypical, proctypical, threadtypical): new functions. - Release: 1.1.32 2009-07-09 Mikio Hirabayashi * tculog.c (tculogwrite, tculrdread): the master ID is now recorded. * ttserver.c (main): the default value of the "-ulim" option was modified. * ttserver.c (proc): update logging and replicaion are now forbidden without the SID. * ttserver.c (do_repl): the NOP command is now throwed while circular replication. - Release: 1.1.31 2009-07-05 Mikio Hirabayashi * scrext.c (scrextkill): new function. * scrext.c (serv_lock, serv_unlock): lock mechanism was modified with TCMDB. - Release: 1.1.30 2009-05-28 Mikio Hirabayashi * ttserver.c (do_http_options): new function. * tcrtest.c (proctable): a test of full-text search was added. - Release: 1.1.29 2009-05-20 Mikio Hirabayashi * ttserver.c (do_task): a potential bug of memory leak was fixed. * tcrdb.c (tcrdbnew): a bug related to initialization was fixed. * scrext.c (serv_fwmkeys, serv_regex): new functions. - Release: 1.1.28 2009-05-11 Mikio Hirabayashi * tcrtest.c (runtable, proctable): "-exp" option was added. * ttskelnull.c: new file. * tokyotyrant.idl: new file. - Release: 1.1.27 2009-05-09 Mikio Hirabayashi * ttutil.c (ttservsettermhandler): new function. * ttserver.c (do_term): new function. - Release: 1.1.26 2009-05-06 Mikio Hirabayashi * ttutil.c (ttcmdidtostr): new function. * tculog.c (tcreplopen): a bug of data size of the server ID was fixed. * tcrdb.c (tcrdbsetmst, tcrdbsetmst2): the timestamp argument was added. * tcrmgr.c (procrepl): command names are now printed. * ttulmgr.c (procexport): command names are now printed. - Release: 1.1.25 2009-04-25 Mikio Hirabayashi * ttutil.c (ttbreakservexpr): new function. * tcrdb.c (tcrdbopen2, tcrdbsetmst2, tcrdbtune): new functions. * tcrdb.c (tcrdbsetecode, tcrdblockmethod, tcrdbunlockmethod): new functions. * ttserver.c (proc): dynamic linking for the skeleton database was added. * ttskelmock.c, ttskelproxy.c: new files. - Release: 1.1.24 2009-04-07 Mikio Hirabayashi * scrext.c (serv_eval, serv_split): new functions. * ttserver.c (do_repl): efficiency on dual-master replication was improved. * ttserver.c (do_optimize): new function. * ttserver.c (do_log): message format was modified. * tcrdb.c (tcrdboptimize): new function. * tcrdb.c (tcrdbvanish, tcrdbcopy, tcrdbrestore): each magic number was drifted. * tcrdb.c (tcrdbrestore, tcrdbsetmst): an argument for options was added. * tculog.c (tculogadbredo): the flag of consistency checking was separated. - Release: 1.1.23 2009-04-06 Mikio Hirabayashi * scrext.c (serv_mapreduce): the parameter for the target keys became an option. - Release: 1.1.22 2009-04-06 Mikio Hirabayashi * scrext.c (serv_mapreduce): the parameter for the session ID was abolished. - Release: 1.1.21 2009-04-01 Mikio Hirabayashi * configure.in: a bug related to support for traditional shells was fixed. * scrext.c (serv_stashputkeep, serv_stashputcat): new functions. * scrext.c (serv_mapreduce, serv_mapreducemapemit): new functions. * ttserver.c (do_put, do_out, do_get): parameter validation was enhanced. - Release: 1.1.20 2009-03-14 Mikio Hirabayashi * tcrdb.c (tcrdbqrysearchget): the option for no update log was added. * tcrdb.c (tcrdbqrysearchcount): new function. * tculog.c (tcreplread): timeout mechanism was added. * ttserver.c (do_repl): performance was improved. - Release: 1.1.19 2009-03-14 Mikio Hirabayashi * ttservctl: configuration was modified and update log is now disabled by default. * ttutil.c (ttopensock, ttacceptsock): performance was improved. * scrext.c (scrextnew): a parameter for the logger was added. - Release: 1.1.18 2009-02-19 Mikio Hirabayashi * ttutil.c (tthttpfetch): timeout mechanism was added. * ttserver.c (do_mc_delete): mismatch of the error message was resolved. * ttserver.c (do_mc_incr, do_mc_decr): behavior became as with the original memcached. * tcrdb.c (tcrdbqrysetlimit): new function instead of "tcrdbqrysetmax". - Release: 1.1.17 2009-02-16 Mikio Hirabayashi * tcrdb.h: the macro "RDBITOPT" was added. * mycond.h, mycond.c, ttutil.c: Solaris portability was added thanks to tamtam. - Release: 1.1.16 2009-02-13 Mikio Hirabayashi * ttutil.c (ttsockgets2): new function. * ttserver (do_task): the size limit of each query was cleared. * tcrdb.c (tcrdbqrysearchget): "column" expression was changed to "get". - Release: 1.1.15 2009-02-05 Mikio Hirabayashi * ttutil.c (ttservstart, ttservaddtimedhandler): multiple tasks are now supported. * ttutil.c (ttsockrecv, ttsockgetint32, ttsockgetint64): performance was improved. * ttserver.c (do_extpc): new function. - Release: 1.1.14 2009-02-04 Mikio Hirabayashi * tcrdb.c (tcrdbqrysearchout): a bug related to the protocol format was fixed. * tcrdb.c (tcrdbqrysearchget): new function. - Release: 1.1.13 2009-01-03 Mikio Hirabayashi * myconf.c (nan): xBSDs are now supported. * ttserver (proc): parameter checking and warning were added. * tcrmgr.c (runmisc, procmisc): table database is now supported. * tcrdb.c (tcrdbtblput, tcrdbtblout, tcrdbtblget): new functions. * tcrdb.c (tcrdbtblsetindex, tcrdbtblgenuid, tcrdbqrysearch): new functions. - Release: 1.1.12 2008-12-08 Mikio Hirabayashi * tcrdb.c (tcrdbmisc): the return value of the "getlist" function was modified. * scrext.c (serv_misc): new function. - Release: 1.1.11 2008-12-08 Mikio Hirabayashi * ttutil.c (tcsocksetlife): new function. * scrext.c (scrextnew): "_thnum" parameter was added. * scrext.c (serv_foreach, serv_stashvanish, serv_stashforeach): new functions. * ttserver.c (do_misc): new function. * tculog.c (tculogadbmisc): new function. - Release: 1.1.10 2008-12-02 Mikio Hirabayashi * tcrdb.c (tcrdbputshl): new function instead of "tcrdbputrtt". * scrext.c (serv_isect, serv_union): new functions. - Release: 1.1.9 2008-10-30 Mikio Hirabayashi * scrext.c (serv_lock, serv_unlock): new functions. * usherette.lua: new file. - Release: 1.1.8 2008-10-20 Mikio Hirabayashi * ttserver.c (getcmdmask): new function. - Release: 1.1.7 2008-10-14 Mikio Hirabayashi * ttutil.h (ttpackdouble, ttunpackdouble): bugs of endian normalization were fixed. - Release: 1.1.6 2008-10-07 Mikio Hirabayashi * scrext.c (serv_codec, serv_hash, serv_sleep, serv_stat, serv_glob): new functions. - Release: 1.1.5 2008-09-28 Mikio Hirabayashi * ttutil.c (ttacceptsock, ttgethostaddr): a bug related to byte order was fixed. - Release: 1.1.4 2008-09-23 Mikio Hirabayashi * ttutil.c (ttdaemonize, ttgetloadavg): new functions. * tcrmgr.c (runimporttsv, procimporttsv): new functions. * scrext.c (serv_time): new function. * ttserver.c (do_stat): the byte order of the server is now sent. * ttserver.c (do_http_put): option to switch functions to be called was added. * ttserver.c (do_http_post): modified to call a function of the language extension. * myconf.c (_tt_epoll_ctl): a potential bug related to misuse of kqueue was fixed. - Release: 1.1.3 2008-09-22 Mikio Hirabayashi * scrext.c (serv_log): new function. - Release: 1.1.2 2008-09-20 Mikio Hirabayashi * scrext.c (serv_stashput, serv_stashout, serv_stashget): new functions. - Release: 1.1.1 2008-09-10 Mikio Hirabayashi * scrext.h, scrext.c: new file. * tculog.c (tculogadbaddint, tculogadbadddouble): new functions. * ttserver.c (do_ext): new function. * tcrdb.c (tcrdbext, tcrdbext2): new functions. - Release: 1.1.0 2008-09-05 Mikio Hirabayashi * ttutil.c (ttpackdouble, ttunpackdouble): new functions. * tcrdb.c (tcrdbaddint, tcrdbadddouble): new functions. - Release: 1.0.1 2008-07-29 Mikio Hirabayashi * myconf.c (_tt_epoll_create, _tt_epoll_ctl, _tt_epoll_wait): new functions. - Release: 1.0.0 2008-07-14 Mikio Hirabayashi * tculog.c (tculogrmtxidx): a bug related to type cast was fixed. * ttserver.c (recmtxidx): a bug related to type cast was fixed. * ttserver.c (do_putrtt): new function. * tcrdb.c (tcrdbputrtt, tcrdbputrtt2): new functions. - Release: 0.9.19 2008-07-10 Mikio Hirabayashi * ttserver.c (do_fwmkeys): a bug related to the "max" parameter was fixed. - Release: 0.9.18 2008-06-26 Mikio Hirabayashi * tcrdb.c (tcrdbputnr, tcrdbputnr2): new functions. * ttserver.c (do_putnr): new functions. - Release: 0.9.17 2008-06-17 Mikio Hirabayashi * tculog.c (tculogbegin): hash locking is now supported. - Release: 0.9.16 2008-06-14 Mikio Hirabayashi * tculog.c (tculogsetaio): new function. * tculog.c (tculogwrite, tculrdread): the system call "aio_write" is now supported. - Release: 0.9.15 2008-05-24 Mikio Hirabayashi * ttutil.c, tculog.c, ttserver.c, tcrdb.c: "free" was replaced by "tcfree". * ttutil.c (ttsockpfsiz): new function. * ttutil.c (tchttpfetch): new function. * ttutil.c (ttservdeqtasks): noreply tasks is now supported. * ttserver.c (DEFTHNUM): the default number of threads was increased. * ttserver.c (do_mc_set, do_mc_add, do_mc_replace, do_mc_delete): "noreply" was added. - Release: 0.9.14 2008-03-13 Mikio Hirabayashi * myconf.h: code cleanup. - Release: 0.9.13 2008-03-13 Mikio Hirabayashi * ttserver.c (main): "-ln" option was abolished. * ttutil.c (ttacceptsock): keep-alive option was added. * ttulmgr.c: new file. * ttservctl (start): "ulimit -n" is now used. - Release: 0.9.12 2008-03-12 Mikio Hirabayashi * ttutil.c (ttservdeqtasks): a bug related to signal handling was fixed. - Release: 0.9.11 2008-03-12 Mikio Hirabayashi * ttutil.c (ttopenservsock, ttacceptsock): SO_LINGER option was removed. * ttserver.c (main, do_log): logging options were added. * ttserver.c (do_stat): file descriptor was to be output. * ttrtest.c (runwrite, procwrite, runread, procread): "-cnum" option was added. - Release: 0.9.10 2008-03-06 Mikio Hirabayashi * ttserver.c (proc): signal handler options were added. - Release: 0.9.9 2008-02-27 Mikio Hirabayashi * ttutil.c (ttservstart, ttservdeqtasks): EPOLLONESHOT is now used. * tcrtest.c (runwrite, procwrite): "-rnd" option was added. - Release: 0.9.8 2008-02-18 Mikio Hirabayashi * ttutil.c (ttopensockunix, ttopenservsockunix): new functions. * ttutil.c (ttservstart): unix domain socket is now suppored. * tcrdb.c (tcrdbopen): unix domain socket is now suppored. * tcrdb.c (tcrdbfwmkeys, tcrdbfwmkeys2): new functions. - Release: 0.9.7 2008-02-08 Mikio Hirabayashi * ttserver.c (do_timed, do_repl): new functions. * ttservctl: new file. - Release: 0.9.6 2008-02-02 Mikio Hirabayashi * tcrdb.c (tcrdbiterinit, tcrdbiternext): new functions. * tcrdb.c (tcrdbrestore): new function. - Release: 0.9.5 2008-01-30 Mikio Hirabayashi * tcrdb.c (tcrdbsync, tcrdbcopy): new functions. * tcrtest.c (runsync, procsync, runcopy, proccopy): new functions. * tculog.c, tcltest.c: new files. - Release: 0.9.4 2008-01-25 Mikio Hirabayashi * ttserver.c (do_mget): new function. * tcrdb.c (tcrdbget3): new function. - Release: 0.9.3 2008-01-24 Mikio Hirabayashi * ttserver.c (do_vsiz, do_http_head, do_http_post): new functions. - Release: 0.9.2 2008-01-24 Mikio Hirabayashi * ttserver.c (do_http_get, do_http_put, do_http_delete): new functions. - Release: 0.9.1 2008-01-20 Mikio Hirabayashi - The initial version. - Release: 0.9.0 tokyotyrant-1.1.40/Makefile.in000066400000000000000000000267611133351147200162650ustar00rootroot00000000000000# Makefile for Tokyo Tyrant #================================================================ # Setting Variables #================================================================ # Generic settings SHELL = @SHELL@ # Package information PACKAGE = @PACKAGE_NAME@ VERSION = @PACKAGE_VERSION@ PACKAGEDIR = $(PACKAGE)-$(VERSION) PACKAGETGZ = $(PACKAGE)-$(VERSION).tar.gz LIBVER = @MYLIBVER@ LIBREV = @MYLIBREV@ PROTVER = @MYPROTVER@ # Targets HEADERFILES = @MYHEADERFILES@ LIBRARYFILES = @MYLIBRARYFILES@ LIBOBJFILES = @MYLIBOBJFILES@ COMMANDFILES = @MYCOMMANDFILES@ INITFILES = @MYINITFILES@ MAN1FILES = @MYMAN1FILES@ MAN3FILES = @MYMAN3FILES@ MAN8FILES = @MYMAN8FILES@ SCREXTFILES = @MYSCREXTFILES@ DOCUMENTFILES = @MYDOCUMENTFILES@ PCFILES = @MYPCFILES@ # Install destinations prefix = @prefix@ exec_prefix = @exec_prefix@ datarootdir = @datarootdir@ INCLUDEDIR = @includedir@ LIBDIR = @libdir@ BINDIR = @bindir@ SBINDIR = @sbindir@ LIBEXECDIR = @libexecdir@ DATADIR = @datadir@/$(PACKAGE) MAN1DIR = @mandir@/man1 MAN3DIR = @mandir@/man3 MAN8DIR = @mandir@/man8 PCDIR = @libdir@/pkgconfig DESTDIR = # Building configuration CC = @CC@ CPPFLAGS = @MYCPPFLAGS@ \ -D_TT_PREFIX="\"$(prefix)\"" -D_TT_INCLUDEDIR="\"$(INCLUDEDIR)\"" \ -D_TT_LIBDIR="\"$(LIBDIR)\"" -D_TT_BINDIR="\"$(BINDIR)\"" -D_TT_LIBEXECDIR="\"$(LIBEXECDIR)\"" \ -D_TT_APPINC="\"-I$(INCLUDEDIR)\"" -D_TT_APPLIBS="\"-L$(LIBDIR) -ltokyotyrant @LIBS@\"" CFLAGS = @MYCFLAGS@ LDFLAGS = @MYLDFLAGS@ CMDLDFLAGS = @MYCMDLDFLAGS@ LIBS = @LIBS@ LDENV = LD_RUN_PATH=/lib:/usr/lib:$(LIBDIR):$(HOME)/lib:/usr/local/lib:@MYRUNPATH@:. RUNENV = @MYLDLIBPATHENV@=.:/lib:/usr/lib:$(LIBDIR):$(HOME)/lib:/usr/local/lib:@MYRUNPATH@ POSTCMD = @MYPOSTCMD@ #================================================================ # Suffix rules #================================================================ .SUFFIXES : .SUFFIXES : .c .o .c.o : $(CC) -c $(CPPFLAGS) $(CFLAGS) $< #================================================================ # Actions #================================================================ all : $(LIBRARYFILES) $(COMMANDFILES) @$(POSTCMD) @printf '\n' @printf '#================================================================\n' @printf '# Ready to install.\n' @printf '#================================================================\n' clean : rm -rf $(LIBRARYFILES) $(LIBOBJFILES) $(COMMANDFILES) \ *.o a.out check.in check.out gmon.out *.vlog words.tsv \ casket casket-* casket.* *.tch *.tcb *.tcf *.tct *.idx.* \ *.ulog ulog 1978* 1979* *.rts *.pid *~ hoge moge tako ika version : vernum=`expr $(LIBVER)00 + $(LIBREV)` ; \ sed -e 's/_TT_VERSION.*/_TT_VERSION "$(VERSION)"/' \ -e "s/_TT_LIBVER.*/_TT_LIBVER $$vernum/" \ -e 's/_TT_PROTVER.*/_TT_PROTVER "$(PROTVER)"/' ttutil.h > ttutil.h~ [ -f ttutil.h~ ] && mv -f ttutil.h~ ttutil.h untabify : ls *.c *.h | while read name ; \ do \ sed -e 's/\t/ /g' -e 's/ *$$//' $$name > $$name~; \ [ -f $$name~ ] && mv -f $$name~ $$name ; \ done install : mkdir -p $(DESTDIR)$(INCLUDEDIR) cp -Rf $(HEADERFILES) $(DESTDIR)$(INCLUDEDIR) mkdir -p $(DESTDIR)$(LIBDIR) cp -Rf $(LIBRARYFILES) $(DESTDIR)$(LIBDIR) mkdir -p $(DESTDIR)$(BINDIR) cp -Rf $(COMMANDFILES) $(DESTDIR)$(BINDIR) mkdir -p $(DESTDIR)$(SBINDIR) cp -Rf $(INITFILES) $(DESTDIR)$(SBINDIR) mkdir -p $(DESTDIR)$(DATADIR) cp -Rf $(SCREXTFILES) $(DOCUMENTFILES) $(DESTDIR)$(DATADIR) mkdir -p $(DESTDIR)$(MAN1DIR) cd man && cp -Rf $(MAN1FILES) $(DESTDIR)$(MAN1DIR) mkdir -p $(DESTDIR)$(MAN3DIR) cd man && cp -Rf $(MAN3FILES) $(DESTDIR)$(MAN3DIR) mkdir -p $(DESTDIR)$(MAN8DIR) cd man && cp -Rf $(MAN8FILES) $(DESTDIR)$(MAN8DIR) mkdir -p $(DESTDIR)$(PCDIR) cp -Rf $(PCFILES) $(DESTDIR)$(PCDIR) @printf '\n' @printf '#================================================================\n' @printf '# Thanks for using Tokyo Tyrant.\n' @printf '#================================================================\n' install-strip : make DESTDIR=$(DESTDIR) install cd $(DESTDIR)$(BINDIR) && strip $(MYCOMMANDS) uninstall : cd $(DESTDIR)$(INCLUDEDIR) && rm -f $(HEADERFILES) cd $(DESTDIR)$(LIBDIR) && rm -f $(LIBRARYFILES) cd $(DESTDIR)$(BINDIR) && rm -f $(COMMANDFILES) cd $(DESTDIR)$(SBINDIR) && rm -f $(INITFILES) cd $(DESTDIR)$(MAN1DIR) && rm -f $(MAN1FILES) cd $(DESTDIR)$(MAN3DIR) && rm -f $(MAN3FILES) cd $(DESTDIR)$(MAN8DIR) && rm -f $(MAN8FILES) rm -rf $(DESTDIR)$(DATADIR) cd $(DESTDIR)$(PCDIR) && rm -f $(PCFILES) dist : make version make untabify make distclean cd .. && tar cvf - $(PACKAGEDIR) | gzip -c > $(PACKAGETGZ) sync ; sync distclean : clean cd example && make clean rm -rf Makefile tokyotyrant.pc config.cache config.log config.status autom4te.cache check : $(RUNENV) $(RUNCMD) ./tcrmgr version $(RUNENV) $(RUNCMD) ./tcrtest write -cnum 5 -tout 5 -rnd 127.0.0.1 50000 $(RUNENV) $(RUNCMD) ./tcrtest write -cnum 5 -tout 5 -nr -rnd 127.0.0.1 50000 $(RUNENV) $(RUNCMD) ./tcrtest write -cnum 5 -tout 5 127.0.0.1 50000 $(RUNENV) $(RUNCMD) ./tcrtest read -cnum 5 -tout 5 127.0.0.1 $(RUNENV) $(RUNCMD) ./tcrtest read -cnum 5 -tout 5 -mul 5 127.0.0.1 $(RUNENV) $(RUNCMD) ./tcrtest remove -cnum 5 -tout 5 127.0.0.1 $(RUNENV) $(RUNCMD) ./tcrtest rcat -cnum 5 -tout 5 127.0.0.1 50000 $(RUNENV) $(RUNCMD) ./tcrtest rcat -cnum 5 -tout 5 -shl 50 127.0.0.1 50000 $(RUNENV) $(RUNCMD) ./tcrmgr vanish 127.0.0.1 $(RUNENV) $(RUNCMD) ./tcrtest rcat -cnum 5 -tout 5 -dad 127.0.0.1 50000 $(RUNENV) $(RUNCMD) ./tcrtest rcat -cnum 5 -tout 5 -ext putcat -xlr 127.0.0.1 50000 $(RUNENV) $(RUNCMD) ./tcrtest misc -cnum 5 -tout 5 127.0.0.1 5000 $(RUNENV) $(RUNCMD) ./tcrtest wicked -cnum 5 -tout 5 127.0.0.1 5000 $(RUNENV) $(RUNCMD) ./tcrmgr inform 127.0.0.1 $(RUNENV) $(RUNCMD) ./tcrmgr vanish 127.0.0.1 $(RUNENV) $(RUNCMD) ./tcrmttest write -tnum 5 127.0.0.1 5000 $(RUNENV) $(RUNCMD) ./tcrmttest read -tnum 5 127.0.0.1 $(RUNENV) $(RUNCMD) ./tcrmttest remove -tnum 5 127.0.0.1 $(RUNENV) $(RUNCMD) ./tcrmttest write -tnum 5 -ext putcat -rnd 127.0.0.1 5000 $(RUNENV) $(RUNCMD) ./tcrmttest typical -tnum 5 127.0.0.1 5000 $(RUNENV) $(RUNCMD) ./tcrmgr vanish 127.0.0.1 $(RUNENV) $(RUNCMD) ./tcrmgr put 127.0.0.1 one first $(RUNENV) $(RUNCMD) ./tcrmgr put 127.0.0.1 two second $(RUNENV) $(RUNCMD) ./tcrmgr put -dk 127.0.0.1 three third $(RUNENV) $(RUNCMD) ./tcrmgr put -dc 127.0.0.1 three third $(RUNENV) $(RUNCMD) ./tcrmgr put -dc 127.0.0.1 three third $(RUNENV) $(RUNCMD) ./tcrmgr put -dc 127.0.0.1 three third $(RUNENV) $(RUNCMD) ./tcrmgr put 127.0.0.1 four fourth $(RUNENV) $(RUNCMD) ./tcrmgr put -dk 127.0.0.1 five fifth $(RUNENV) $(RUNCMD) ./tcrmgr out 127.0.0.1 one $(RUNENV) $(RUNCMD) ./tcrmgr out 127.0.0.1 two $(RUNENV) $(RUNCMD) ./tcrmgr get 127.0.0.1 three > check.out $(RUNENV) $(RUNCMD) ./tcrmgr get 127.0.0.1 four > check.out $(RUNENV) $(RUNCMD) ./tcrmgr get 127.0.0.1 five > check.out $(RUNENV) $(RUNCMD) ./tcrmgr mget 127.0.0.1 one two three four five > check.out $(RUNENV) $(RUNCMD) ./tcrmgr misc 127.0.0.1 putlist six sixth seven seventh $(RUNENV) $(RUNCMD) ./tcrmgr misc 127.0.0.1 outlist six $(RUNENV) $(RUNCMD) ./tcrmgr misc 127.0.0.1 getlist three four five six > check.out $(RUNENV) $(RUNCMD) ./tcrmgr list -pv 127.0.0.1 > check.out $(RUNENV) $(RUNCMD) ./tcrmgr list -pv -fm f 127.0.0.1 > check.out $(RUNENV) $(RUNCMD) ./tcrmgr http -ih http://127.0.0.1:1978/five > check.out rm -rf ulog ; mkdir -p ulog $(RUNENV) $(RUNCMD) ./ttultest write -lim 10000 ulog 5000 $(RUNENV) $(RUNCMD) ./ttultest write -lim 10000 -as ulog 5000 $(RUNENV) $(RUNCMD) ./ttultest read ulog rm -rf ulog ; mkdir -p ulog $(RUNENV) $(RUNCMD) ./ttultest thread -lim 10000 ulog 5 5000 $(RUNENV) $(RUNCMD) ./ttultest thread -lim 10000 -as ulog 5 5000 @printf '\n' @printf '#================================================================\n' @printf '# Checking completed.\n' @printf '#================================================================\n' check-valgrind : make RUNCMD="valgrind --tool=memcheck --log-file=%p.vlog" check grep ERROR *.vlog | grep -v ' 0 errors' ; true grep 'at exit' *.vlog | grep -v ' 0 bytes' ; true check-forever : while true ; \ do \ make check || break ; \ done words : cat -n /usr/share/dict/words | \ sed -e 's/^ *//' -e 'y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/' \ -e 's/^\([0-9]*\)\t\(.*\)/\2\t\1/' > words.tsv ./tcrmgr importtsv localhost words.tsv .PHONY : all clean install check #================================================================ # Building binaries #================================================================ libtokyotyrant.a : $(LIBOBJFILES) $(AR) $(ARFLAGS) $@ $(LIBOBJFILES) libtokyotyrant.so.$(LIBVER).$(LIBREV).0 : $(LIBOBJFILES) if uname -a | egrep -i 'SunOS' > /dev/null ; \ then \ $(CC) $(CFLAGS) -shared -Wl,-G,-h,libtokyotyrant.so.$(LIBVER) -o $@ \ $(LIBOBJFILES) $(LDFLAGS) $(LIBS) ; \ else \ $(CC) $(CFLAGS) -shared -Wl,-soname,libtokyotyrant.so.$(LIBVER) -o $@ \ $(LIBOBJFILES) $(LDFLAGS) $(LIBS) ; \ fi libtokyotyrant.so.$(LIBVER) : libtokyotyrant.so.$(LIBVER).$(LIBREV).0 ln -f -s libtokyotyrant.so.$(LIBVER).$(LIBREV).0 $@ libtokyotyrant.so : libtokyotyrant.so.$(LIBVER).$(LIBREV).0 ln -f -s libtokyotyrant.so.$(LIBVER).$(LIBREV).0 $@ libtokyotyrant.$(LIBVER).$(LIBREV).0.dylib : $(LIBOBJFILES) $(CC) $(CFLAGS) -dynamiclib -o $@ \ -install_name $(LIBDIR)/libtokyotyrant.$(LIBVER).dylib \ -current_version $(LIBVER).$(LIBREV).0 -compatibility_version $(LIBVER) \ $(LIBOBJFILES) $(LDFLAGS) $(LIBS) libtokyotyrant.$(LIBVER).dylib : libtokyotyrant.$(LIBVER).$(LIBREV).0.dylib ln -f -s libtokyotyrant.$(LIBVER).$(LIBREV).0.dylib $@ libtokyotyrant.dylib : libtokyotyrant.$(LIBVER).$(LIBREV).0.dylib ln -f -s libtokyotyrant.$(LIBVER).$(LIBREV).0.dylib $@ ttskelmock.so : ttskelmock.o $(CC) $(CFLAGS) -shared -o $@ $< $(LDFLAGS) $(LIBS) ttskeldir.so : ttskeldir.o $(CC) $(CFLAGS) -shared -o $@ $< $(LDFLAGS) $(LIBS) ttskelproxy.so : ttskelproxy.o $(CC) $(CFLAGS) -shared -o $@ $< $(LDFLAGS) $(LIBS) ttskelnull.so : ttskelnull.o $(CC) $(CFLAGS) -shared -o $@ $< $(LDFLAGS) $(LIBS) ttskelmock.bundle : ttskelmock.o $(CC) $(CFLAGS) -bundle -flat_namespace -undefined suppress -o $@ $< $(LDFLAGS) $(LIBS) ttskeldir.bundle : ttskeldir.o $(CC) $(CFLAGS) -bundle -flat_namespace -undefined suppress -o $@ $< $(LDFLAGS) $(LIBS) ttskelproxy.bundle : ttskelproxy.o $(CC) $(CFLAGS) -bundle -flat_namespace -undefined suppress -o $@ $< $(LDFLAGS) $(LIBS) ttskelnull.bundle : ttskelnull.o $(CC) $(CFLAGS) -bundle -flat_namespace -undefined suppress -o $@ $< $(LDFLAGS) $(LIBS) ttserver : ttserver.o scrext.o $(LIBRARYFILES) $(LDENV) $(CC) $(CFLAGS) -o $@ $< scrext.o $(LDFLAGS) $(CMDLDFLAGS) -ltokyotyrant $(LIBS) ttulmgr : ttulmgr.o $(LIBRARYFILES) $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) $(CMDLDFLAGS) -ltokyotyrant $(LIBS) ttultest : ttultest.o $(LIBRARYFILES) $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) $(CMDLDFLAGS) -ltokyotyrant $(LIBS) tcrtest : tcrtest.o $(LIBRARYFILES) $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) $(CMDLDFLAGS) -ltokyotyrant $(LIBS) tcrmttest : tcrmttest.o $(LIBRARYFILES) $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) $(CMDLDFLAGS) -ltokyotyrant $(LIBS) tcrmgr : tcrmgr.o $(LIBRARYFILES) $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) $(CMDLDFLAGS) -ltokyotyrant $(LIBS) myconf.o scrext.o : myconf.h ttutil.o : myconf.h ttutil.h tculog.o : myconf.h ttutil.h tculog.h tcrdb.o : myconf.h ttutil.h tcrdb.h ttskelproxy.o : myconf.h ttutil.h tcrdb.h ttserver.o ttulmgr.o ttultest.o tcrtest.o tcrmgr.o : myconf.h ttutil.h tculog.h tcrdb.h ttserver.o : scrext.h # END OF FILE tokyotyrant-1.1.40/README000066400000000000000000000021131133351147200150610ustar00rootroot00000000000000================================================================ Tokyo Tyrant: network interface of Tokyo Cabinet Copyright (C) 2007-2009 Mikio Hirabayashi ================================================================ Please read the following documents with a WWW browser. How to install Tokyo Cabinet is explained in the specification. README - this file COPYING - license ChangeLog - history of enhancement THANKS - list of contributors doc/index.html - index of documents Contents of the directory tree is below. ./ - sources of Tokyo Tyrant ./doc/ - manuals and specifications ./man/ - manuals for nroff ./ext/ - sample script for the script extension ./example/ - sample code of tutorial ./lab/ - for test and experiment Tokyo Tyrant is released under the terms of the GNU Lesser General Public License. See the file `COPYING' for details. Tokyo Tyrant was written by Mikio Hirabayashi. You can contact the author by e-mail to `hirarin@gmail.com'. Thanks. == END OF FILE == tokyotyrant-1.1.40/THANKS000066400000000000000000000005631133351147200151230ustar00rootroot00000000000000================================================================ Thanks to all of the following for their valuable suggestions or contributions. ================================================================ mixi Inc. and and its developers - allowing me to develop and publish Tokyo Tyrant as business work tamtam - added Solaris support == END OF FILE == tokyotyrant-1.1.40/configure000077500000000000000000005767651133351147200161450ustar00rootroot00000000000000#! /bin/sh # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.63 for tokyotyrant 1.1.40. # # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, # 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. ## --------------------- ## ## M4sh Initialization. ## ## --------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in *posix*) set -o posix ;; esac fi # PATH needs CR # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo if (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # Support unset when possible. if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then as_unset=unset else as_unset=false fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. case $0 in *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 { (exit 1); exit 1; } fi # Work around bugs in pre-3.0 UWIN ksh. for as_var in ENV MAIL MAILPATH do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # Required to use basename. if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi # Name of the executable. as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # CDPATH. $as_unset CDPATH if test "x$CONFIG_SHELL" = x; then if (eval ":") 2>/dev/null; then as_have_required=yes else as_have_required=no fi if test $as_have_required = yes && (eval ": (as_func_return () { (exit \$1) } as_func_success () { as_func_return 0 } as_func_failure () { as_func_return 1 } as_func_ret_success () { return 0 } as_func_ret_failure () { return 1 } exitcode=0 if as_func_success; then : else exitcode=1 echo as_func_success failed. fi if as_func_failure; then exitcode=1 echo as_func_failure succeeded. fi if as_func_ret_success; then : else exitcode=1 echo as_func_ret_success failed. fi if as_func_ret_failure; then exitcode=1 echo as_func_ret_failure succeeded. fi if ( set x; as_func_ret_success y && test x = \"\$1\" ); then : else exitcode=1 echo positional parameters were not saved. fi test \$exitcode = 0) || { (exit 1); exit 1; } ( as_lineno_1=\$LINENO as_lineno_2=\$LINENO test \"x\$as_lineno_1\" != \"x\$as_lineno_2\" && test \"x\`expr \$as_lineno_1 + 1\`\" = \"x\$as_lineno_2\") || { (exit 1); exit 1; } ") 2> /dev/null; then : else as_candidate_shells= as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. case $as_dir in /*) for as_base in sh bash ksh sh5; do as_candidate_shells="$as_candidate_shells $as_dir/$as_base" done;; esac done IFS=$as_save_IFS for as_shell in $as_candidate_shells $SHELL; do # Try only shells that exist, to save several forks. if { test -f "$as_shell" || test -f "$as_shell.exe"; } && { ("$as_shell") 2> /dev/null <<\_ASEOF if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in *posix*) set -o posix ;; esac fi : _ASEOF }; then CONFIG_SHELL=$as_shell as_have_required=yes if { "$as_shell" 2> /dev/null <<\_ASEOF if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in *posix*) set -o posix ;; esac fi : (as_func_return () { (exit $1) } as_func_success () { as_func_return 0 } as_func_failure () { as_func_return 1 } as_func_ret_success () { return 0 } as_func_ret_failure () { return 1 } exitcode=0 if as_func_success; then : else exitcode=1 echo as_func_success failed. fi if as_func_failure; then exitcode=1 echo as_func_failure succeeded. fi if as_func_ret_success; then : else exitcode=1 echo as_func_ret_success failed. fi if as_func_ret_failure; then exitcode=1 echo as_func_ret_failure succeeded. fi if ( set x; as_func_ret_success y && test x = "$1" ); then : else exitcode=1 echo positional parameters were not saved. fi test $exitcode = 0) || { (exit 1); exit 1; } ( as_lineno_1=$LINENO as_lineno_2=$LINENO test "x$as_lineno_1" != "x$as_lineno_2" && test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2") || { (exit 1); exit 1; } _ASEOF }; then break fi fi done if test "x$CONFIG_SHELL" != x; then for as_var in BASH_ENV ENV do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var done export CONFIG_SHELL exec "$CONFIG_SHELL" "$as_myself" ${1+"$@"} fi if test $as_have_required = no; then echo This script requires a shell more modern than all the echo shells that I found on your system. Please install a echo modern shell, or manually run the script under such a echo shell if you do have one. { (exit 1); exit 1; } fi fi fi (eval "as_func_return () { (exit \$1) } as_func_success () { as_func_return 0 } as_func_failure () { as_func_return 1 } as_func_ret_success () { return 0 } as_func_ret_failure () { return 1 } exitcode=0 if as_func_success; then : else exitcode=1 echo as_func_success failed. fi if as_func_failure; then exitcode=1 echo as_func_failure succeeded. fi if as_func_ret_success; then : else exitcode=1 echo as_func_ret_success failed. fi if as_func_ret_failure; then exitcode=1 echo as_func_ret_failure succeeded. fi if ( set x; as_func_ret_success y && test x = \"\$1\" ); then : else exitcode=1 echo positional parameters were not saved. fi test \$exitcode = 0") || { echo No shell found that supports shell functions. echo Please tell bug-autoconf@gnu.org about your system, echo including any error possibly output before this message. echo This can help us improve future autoconf versions. echo Configuration will now proceed without shell functions. } as_lineno_1=$LINENO as_lineno_2=$LINENO test "x$as_lineno_1" != "x$as_lineno_2" && test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || { # Create $as_me.lineno as a copy of $as_myself, but with $LINENO # uniformly replaced by the line number. The first 'sed' inserts a # line-number line after each line using $LINENO; the second 'sed' # does the real work. The second script uses 'N' to pair each # line-number line with the line containing $LINENO, and appends # trailing '-' during substitution so that $LINENO is not a special # case at line end. # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the # scripts with optimization help from Paolo Bonzini. Blame Lee # E. McMahon (1931-1989) for sed's syntax. :-) sed -n ' p /[$]LINENO/= ' <$as_myself | sed ' s/[$]LINENO.*/&-/ t lineno b :lineno N :loop s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ t loop s/-\n.*// ' >$as_me.lineno && chmod +x "$as_me.lineno" || { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 { (exit 1); exit 1; }; } # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensitive to this). . "./$as_me.lineno" # Exit status is that of the last command. exit } if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in -n*) case `echo 'x\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. *) ECHO_C='\c';; esac;; *) ECHO_N='-n';; esac if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -p'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -p' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -p' fi else as_ln_s='cp -p' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null if mkdir -p . 2>/dev/null; then as_mkdir_p=: else test -d ./-p && rmdir ./-p as_mkdir_p=false fi if test -x / >/dev/null 2>&1; then as_test_x='test -x' else if ls -dL / >/dev/null 2>&1; then as_ls_L_option=L else as_ls_L_option= fi as_test_x=' eval sh -c '\'' if test -d "$1"; then test -d "$1/."; else case $1 in -*)set "./$1";; esac; case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in ???[sx]*):;;*)false;;esac;fi '\'' sh ' fi as_executable_p=$as_test_x # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" exec 7<&0 &1 # Name of the host. # hostname on some systems (SVR3.2, Linux) returns a bogus exit status, # so uname gets run too. ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` # # Initializations. # ac_default_prefix=/usr/local ac_clean_files= ac_config_libobj_dir=. LIBOBJS= cross_compiling=no subdirs= MFLAGS= MAKEFLAGS= SHELL=${CONFIG_SHELL-/bin/sh} # Identity of this package. PACKAGE_NAME='tokyotyrant' PACKAGE_TARNAME='tokyotyrant' PACKAGE_VERSION='1.1.40' PACKAGE_STRING='tokyotyrant 1.1.40' PACKAGE_BUGREPORT='' # Factoring default headers for most tests. ac_includes_default="\ #include #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #ifdef STDC_HEADERS # include # include #else # ifdef HAVE_STDLIB_H # include # endif #endif #ifdef HAVE_STRING_H # if !defined STDC_HEADERS && defined HAVE_MEMORY_H # include # endif # include #endif #ifdef HAVE_STRINGS_H # include #endif #ifdef HAVE_INTTYPES_H # include #endif #ifdef HAVE_STDINT_H # include #endif #ifdef HAVE_UNISTD_H # include #endif" ac_subst_vars='LTLIBOBJS LIBOBJS MYPOSTCMD MYLDLIBPATHENV MYRUNPATH MYCMDLDFLAGS MYLDFLAGS MYCPPFLAGS MYCFLAGS MYPCFILES MYDOCUMENTFILES MYSCREXTFILES MYMAN8FILES MYMAN3FILES MYMAN1FILES MYINITFILES MYCOMMANDFILES MYLIBOBJFILES MYLIBRARYFILES MYHEADERFILES MYPROTVER MYLIBREV MYLIBVER EGREP GREP CPP OBJEXT EXEEXT ac_ct_CC CPPFLAGS LDFLAGS CFLAGS CC target_alias host_alias build_alias LIBS ECHO_T ECHO_N ECHO_C DEFS mandir localedir libdir psdir pdfdir dvidir htmldir infodir docdir oldincludedir includedir localstatedir sharedstatedir sysconfdir datadir datarootdir libexecdir sbindir bindir program_transform_name prefix exec_prefix PACKAGE_BUGREPORT PACKAGE_STRING PACKAGE_VERSION PACKAGE_TARNAME PACKAGE_NAME PATH_SEPARATOR SHELL' ac_subst_files='' ac_user_opts=' enable_option_checking enable_debug enable_devel enable_profile enable_static enable_shared enable_lua with_tc with_zlib with_bzip with_lua ' ac_precious_vars='build_alias host_alias target_alias CC CFLAGS LDFLAGS LIBS CPPFLAGS CPP' # Initialize some variables set by options. ac_init_help= ac_init_version=false ac_unrecognized_opts= ac_unrecognized_sep= # The variables have the same names as the options, with # dashes changed to underlines. cache_file=/dev/null exec_prefix=NONE no_create= no_recursion= prefix=NONE program_prefix=NONE program_suffix=NONE program_transform_name=s,x,x, silent= site= srcdir= verbose= x_includes=NONE x_libraries=NONE # Installation directory options. # These are left unexpanded so users can "make install exec_prefix=/foo" # and all the variables that are supposed to be based on exec_prefix # by default will actually change. # Use braces instead of parens because sh, perl, etc. also accept them. # (The list follows the same order as the GNU Coding Standards.) bindir='${exec_prefix}/bin' sbindir='${exec_prefix}/sbin' libexecdir='${exec_prefix}/libexec' datarootdir='${prefix}/share' datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' infodir='${datarootdir}/info' htmldir='${docdir}' dvidir='${docdir}' pdfdir='${docdir}' psdir='${docdir}' libdir='${exec_prefix}/lib' localedir='${datarootdir}/locale' mandir='${datarootdir}/man' ac_prev= ac_dashdash= for ac_option do # If the previous option needs an argument, assign it. if test -n "$ac_prev"; then eval $ac_prev=\$ac_option ac_prev= continue fi case $ac_option in *=*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; *) ac_optarg=yes ;; esac # Accept the important Cygnus configure options, so we can diagnose typos. case $ac_dashdash$ac_option in --) ac_dashdash=yes ;; -bindir | --bindir | --bindi | --bind | --bin | --bi) ac_prev=bindir ;; -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) bindir=$ac_optarg ;; -build | --build | --buil | --bui | --bu) ac_prev=build_alias ;; -build=* | --build=* | --buil=* | --bui=* | --bu=*) build_alias=$ac_optarg ;; -cache-file | --cache-file | --cache-fil | --cache-fi \ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) ac_prev=cache_file ;; -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) cache_file=$ac_optarg ;; --config-cache | -C) cache_file=config.cache ;; -datadir | --datadir | --datadi | --datad) ac_prev=datadir ;; -datadir=* | --datadir=* | --datadi=* | --datad=*) datadir=$ac_optarg ;; -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ | --dataroo | --dataro | --datar) ac_prev=datarootdir ;; -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) datarootdir=$ac_optarg ;; -disable-* | --disable-*) ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && { $as_echo "$as_me: error: invalid feature name: $ac_useropt" >&2 { (exit 1); exit 1; }; } ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=no ;; -docdir | --docdir | --docdi | --doc | --do) ac_prev=docdir ;; -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) docdir=$ac_optarg ;; -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) ac_prev=dvidir ;; -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) dvidir=$ac_optarg ;; -enable-* | --enable-*) ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && { $as_echo "$as_me: error: invalid feature name: $ac_useropt" >&2 { (exit 1); exit 1; }; } ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=\$ac_optarg ;; -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ | --exec | --exe | --ex) ac_prev=exec_prefix ;; -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ | --exec=* | --exe=* | --ex=*) exec_prefix=$ac_optarg ;; -gas | --gas | --ga | --g) # Obsolete; use --with-gas. with_gas=yes ;; -help | --help | --hel | --he | -h) ac_init_help=long ;; -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) ac_init_help=recursive ;; -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) ac_init_help=short ;; -host | --host | --hos | --ho) ac_prev=host_alias ;; -host=* | --host=* | --hos=* | --ho=*) host_alias=$ac_optarg ;; -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) ac_prev=htmldir ;; -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ | --ht=*) htmldir=$ac_optarg ;; -includedir | --includedir | --includedi | --included | --include \ | --includ | --inclu | --incl | --inc) ac_prev=includedir ;; -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ | --includ=* | --inclu=* | --incl=* | --inc=*) includedir=$ac_optarg ;; -infodir | --infodir | --infodi | --infod | --info | --inf) ac_prev=infodir ;; -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) infodir=$ac_optarg ;; -libdir | --libdir | --libdi | --libd) ac_prev=libdir ;; -libdir=* | --libdir=* | --libdi=* | --libd=*) libdir=$ac_optarg ;; -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ | --libexe | --libex | --libe) ac_prev=libexecdir ;; -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ | --libexe=* | --libex=* | --libe=*) libexecdir=$ac_optarg ;; -localedir | --localedir | --localedi | --localed | --locale) ac_prev=localedir ;; -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) localedir=$ac_optarg ;; -localstatedir | --localstatedir | --localstatedi | --localstated \ | --localstate | --localstat | --localsta | --localst | --locals) ac_prev=localstatedir ;; -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) localstatedir=$ac_optarg ;; -mandir | --mandir | --mandi | --mand | --man | --ma | --m) ac_prev=mandir ;; -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) mandir=$ac_optarg ;; -nfp | --nfp | --nf) # Obsolete; use --without-fp. with_fp=no ;; -no-create | --no-create | --no-creat | --no-crea | --no-cre \ | --no-cr | --no-c | -n) no_create=yes ;; -no-recursion | --no-recursion | --no-recursio | --no-recursi \ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) no_recursion=yes ;; -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ | --oldin | --oldi | --old | --ol | --o) ac_prev=oldincludedir ;; -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) oldincludedir=$ac_optarg ;; -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) ac_prev=prefix ;; -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) prefix=$ac_optarg ;; -program-prefix | --program-prefix | --program-prefi | --program-pref \ | --program-pre | --program-pr | --program-p) ac_prev=program_prefix ;; -program-prefix=* | --program-prefix=* | --program-prefi=* \ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) program_prefix=$ac_optarg ;; -program-suffix | --program-suffix | --program-suffi | --program-suff \ | --program-suf | --program-su | --program-s) ac_prev=program_suffix ;; -program-suffix=* | --program-suffix=* | --program-suffi=* \ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) program_suffix=$ac_optarg ;; -program-transform-name | --program-transform-name \ | --program-transform-nam | --program-transform-na \ | --program-transform-n | --program-transform- \ | --program-transform | --program-transfor \ | --program-transfo | --program-transf \ | --program-trans | --program-tran \ | --progr-tra | --program-tr | --program-t) ac_prev=program_transform_name ;; -program-transform-name=* | --program-transform-name=* \ | --program-transform-nam=* | --program-transform-na=* \ | --program-transform-n=* | --program-transform-=* \ | --program-transform=* | --program-transfor=* \ | --program-transfo=* | --program-transf=* \ | --program-trans=* | --program-tran=* \ | --progr-tra=* | --program-tr=* | --program-t=*) program_transform_name=$ac_optarg ;; -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) ac_prev=pdfdir ;; -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) pdfdir=$ac_optarg ;; -psdir | --psdir | --psdi | --psd | --ps) ac_prev=psdir ;; -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) psdir=$ac_optarg ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) silent=yes ;; -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ | --sbi=* | --sb=*) sbindir=$ac_optarg ;; -sharedstatedir | --sharedstatedir | --sharedstatedi \ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ | --sharedst | --shareds | --shared | --share | --shar \ | --sha | --sh) ac_prev=sharedstatedir ;; -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ | --sha=* | --sh=*) sharedstatedir=$ac_optarg ;; -site | --site | --sit) ac_prev=site ;; -site=* | --site=* | --sit=*) site=$ac_optarg ;; -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) ac_prev=srcdir ;; -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) srcdir=$ac_optarg ;; -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ | --syscon | --sysco | --sysc | --sys | --sy) ac_prev=sysconfdir ;; -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) sysconfdir=$ac_optarg ;; -target | --target | --targe | --targ | --tar | --ta | --t) ac_prev=target_alias ;; -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) target_alias=$ac_optarg ;; -v | -verbose | --verbose | --verbos | --verbo | --verb) verbose=yes ;; -version | --version | --versio | --versi | --vers | -V) ac_init_version=: ;; -with-* | --with-*) ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && { $as_echo "$as_me: error: invalid package name: $ac_useropt" >&2 { (exit 1); exit 1; }; } ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=\$ac_optarg ;; -without-* | --without-*) ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && { $as_echo "$as_me: error: invalid package name: $ac_useropt" >&2 { (exit 1); exit 1; }; } ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=no ;; --x) # Obsolete; use --with-x. with_x=yes ;; -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ | --x-incl | --x-inc | --x-in | --x-i) ac_prev=x_includes ;; -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) x_includes=$ac_optarg ;; -x-libraries | --x-libraries | --x-librarie | --x-librari \ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) ac_prev=x_libraries ;; -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) x_libraries=$ac_optarg ;; -*) { $as_echo "$as_me: error: unrecognized option: $ac_option Try \`$0 --help' for more information." >&2 { (exit 1); exit 1; }; } ;; *=*) ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` # Reject names that are not valid shell variable names. expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null && { $as_echo "$as_me: error: invalid variable name: $ac_envvar" >&2 { (exit 1); exit 1; }; } eval $ac_envvar=\$ac_optarg export $ac_envvar ;; *) # FIXME: should be removed in autoconf 3.0. $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option} ;; esac done if test -n "$ac_prev"; then ac_option=--`echo $ac_prev | sed 's/_/-/g'` { $as_echo "$as_me: error: missing argument to $ac_option" >&2 { (exit 1); exit 1; }; } fi if test -n "$ac_unrecognized_opts"; then case $enable_option_checking in no) ;; fatal) { $as_echo "$as_me: error: unrecognized options: $ac_unrecognized_opts" >&2 { (exit 1); exit 1; }; } ;; *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; esac fi # Check all directory arguments for consistency. for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ libdir localedir mandir do eval ac_val=\$$ac_var # Remove trailing slashes. case $ac_val in */ ) ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` eval $ac_var=\$ac_val;; esac # Be sure to have absolute directory names. case $ac_val in [\\/$]* | ?:[\\/]* ) continue;; NONE | '' ) case $ac_var in *prefix ) continue;; esac;; esac { $as_echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 { (exit 1); exit 1; }; } done # There might be people who depend on the old broken behavior: `$host' # used to hold the argument of --host etc. # FIXME: To remove some day. build=$build_alias host=$host_alias target=$target_alias # FIXME: To remove some day. if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe $as_echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host. If a cross compiler is detected then cross compile mode will be used." >&2 elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi fi ac_tool_prefix= test -n "$host_alias" && ac_tool_prefix=$host_alias- test "$silent" = yes && exec 6>/dev/null ac_pwd=`pwd` && test -n "$ac_pwd" && ac_ls_di=`ls -di .` && ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || { $as_echo "$as_me: error: working directory cannot be determined" >&2 { (exit 1); exit 1; }; } test "X$ac_ls_di" = "X$ac_pwd_ls_di" || { $as_echo "$as_me: error: pwd does not report name of working directory" >&2 { (exit 1); exit 1; }; } # Find the source files, if location was not specified. if test -z "$srcdir"; then ac_srcdir_defaulted=yes # Try the directory containing this script, then the parent directory. ac_confdir=`$as_dirname -- "$as_myself" || $as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_myself" : 'X\(//\)[^/]' \| \ X"$as_myself" : 'X\(//\)$' \| \ X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_myself" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` srcdir=$ac_confdir if test ! -r "$srcdir/$ac_unique_file"; then srcdir=.. fi else ac_srcdir_defaulted=no fi if test ! -r "$srcdir/$ac_unique_file"; then test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." { $as_echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2 { (exit 1); exit 1; }; } fi ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" ac_abs_confdir=`( cd "$srcdir" && test -r "./$ac_unique_file" || { $as_echo "$as_me: error: $ac_msg" >&2 { (exit 1); exit 1; }; } pwd)` # When building in place, set srcdir=. if test "$ac_abs_confdir" = "$ac_pwd"; then srcdir=. fi # Remove unnecessary trailing slashes from srcdir. # Double slashes in file names in object file debugging info # mess up M-x gdb in Emacs. case $srcdir in */) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; esac for ac_var in $ac_precious_vars; do eval ac_env_${ac_var}_set=\${${ac_var}+set} eval ac_env_${ac_var}_value=\$${ac_var} eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} eval ac_cv_env_${ac_var}_value=\$${ac_var} done # # Report the --help message. # if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF \`configure' configures tokyotyrant 1.1.40 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... To assign environment variables (e.g., CC, CFLAGS...), specify them as VAR=VALUE. See below for descriptions of some of the useful variables. Defaults for the options are specified in brackets. Configuration: -h, --help display this help and exit --help=short display options specific to this package --help=recursive display the short help of all the included packages -V, --version display version information and exit -q, --quiet, --silent do not print \`checking...' messages --cache-file=FILE cache test results in FILE [disabled] -C, --config-cache alias for \`--cache-file=config.cache' -n, --no-create do not create output files --srcdir=DIR find the sources in DIR [configure dir or \`..'] Installation directories: --prefix=PREFIX install architecture-independent files in PREFIX [$ac_default_prefix] --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX [PREFIX] By default, \`make install' will install all the files in \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify an installation prefix other than \`$ac_default_prefix' using \`--prefix', for instance \`--prefix=\$HOME'. For better control, use the options below. Fine tuning of the installation directories: --bindir=DIR user executables [EPREFIX/bin] --sbindir=DIR system admin executables [EPREFIX/sbin] --libexecdir=DIR program executables [EPREFIX/libexec] --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] --datadir=DIR read-only architecture-independent data [DATAROOTDIR] --infodir=DIR info documentation [DATAROOTDIR/info] --localedir=DIR locale-dependent data [DATAROOTDIR/locale] --mandir=DIR man documentation [DATAROOTDIR/man] --docdir=DIR documentation root [DATAROOTDIR/doc/tokyotyrant] --htmldir=DIR html documentation [DOCDIR] --dvidir=DIR dvi documentation [DOCDIR] --pdfdir=DIR pdf documentation [DOCDIR] --psdir=DIR ps documentation [DOCDIR] _ACEOF cat <<\_ACEOF _ACEOF fi if test -n "$ac_init_help"; then case $ac_init_help in short | recursive ) echo "Configuration of tokyotyrant 1.1.40:";; esac cat <<\_ACEOF Optional Features: --disable-option-checking ignore unrecognized --enable/--with options --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --enable-FEATURE[=ARG] include FEATURE [ARG=yes] --enable-debug build for debugging --enable-devel build for development --enable-profile build for profiling --enable-static build by static linking --disable-shared avoid to build shared libraries --enable-lua build with Lua extension Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) --with-tc=DIR search DIR/include and DIR/lib for Tokyo Cabinet --with-zlib=DIR search DIR/include and DIR/lib for ZLIB --with-bzip=DIR search DIR/include and DIR/lib for BZIP2 --with-lua=DIR search DIR/include and DIR/lib for Lua Some influential environment variables: CC C compiler command CFLAGS C compiler flags LDFLAGS linker flags, e.g. -L if you have libraries in a nonstandard directory LIBS libraries to pass to the linker, e.g. -l CPPFLAGS C/C++/Objective C preprocessor flags, e.g. -I if you have headers in a nonstandard directory CPP C preprocessor Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. _ACEOF ac_status=$? fi if test "$ac_init_help" = "recursive"; then # If there are subdirs, report their specific --help. for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue test -d "$ac_dir" || { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || continue ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix cd "$ac_dir" || { ac_status=$?; continue; } # Check for guested configure. if test -f "$ac_srcdir/configure.gnu"; then echo && $SHELL "$ac_srcdir/configure.gnu" --help=recursive elif test -f "$ac_srcdir/configure"; then echo && $SHELL "$ac_srcdir/configure" --help=recursive else $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 fi || ac_status=$? cd "$ac_pwd" || { ac_status=$?; break; } done fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF tokyotyrant configure 1.1.40 generated by GNU Autoconf 2.63 Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF exit fi cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. It was created by tokyotyrant $as_me 1.1.40, which was generated by GNU Autoconf 2.63. Invocation command line was $ $0 $@ _ACEOF exec 5>>config.log { cat <<_ASUNAME ## --------- ## ## Platform. ## ## --------- ## hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` /bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` /bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` /usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` /bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` /bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` _ASUNAME as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. $as_echo "PATH: $as_dir" done IFS=$as_save_IFS } >&5 cat >&5 <<_ACEOF ## ----------- ## ## Core tests. ## ## ----------- ## _ACEOF # Keep a trace of the command line. # Strip out --no-create and --no-recursion so they do not pile up. # Strip out --silent because we don't want to record it for future runs. # Also quote any args containing shell meta-characters. # Make two passes to allow for proper duplicate-argument suppression. ac_configure_args= ac_configure_args0= ac_configure_args1= ac_must_keep_next=false for ac_pass in 1 2 do for ac_arg do case $ac_arg in -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) continue ;; *\'*) ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac case $ac_pass in 1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;; 2) ac_configure_args1="$ac_configure_args1 '$ac_arg'" if test $ac_must_keep_next = true; then ac_must_keep_next=false # Got value, back to normal. else case $ac_arg in *=* | --config-cache | -C | -disable-* | --disable-* \ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ | -with-* | --with-* | -without-* | --without-* | --x) case "$ac_configure_args0 " in "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; esac ;; -* ) ac_must_keep_next=true ;; esac fi ac_configure_args="$ac_configure_args '$ac_arg'" ;; esac done done $as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; } $as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; } # When interrupted or exit'd, cleanup temporary files, and complete # config.log. We remove comments because anyway the quotes in there # would cause problems or look ugly. # WARNING: Use '\'' to represent an apostrophe within the trap. # WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. trap 'exit_status=$? # Save into config.log some information that might help in debugging. { echo cat <<\_ASBOX ## ---------------- ## ## Cache variables. ## ## ---------------- ## _ASBOX echo # The following way of writing the cache mishandles newlines in values, ( for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { $as_echo "$as_me:$LINENO: WARNING: cache variable $ac_var contains a newline" >&5 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) $as_unset $ac_var ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( *${as_nl}ac_space=\ *) sed -n \ "s/'\''/'\''\\\\'\'''\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" ;; #( *) sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) echo cat <<\_ASBOX ## ----------------- ## ## Output variables. ## ## ----------------- ## _ASBOX echo for ac_var in $ac_subst_vars do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac $as_echo "$ac_var='\''$ac_val'\''" done | sort echo if test -n "$ac_subst_files"; then cat <<\_ASBOX ## ------------------- ## ## File substitutions. ## ## ------------------- ## _ASBOX echo for ac_var in $ac_subst_files do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac $as_echo "$ac_var='\''$ac_val'\''" done | sort echo fi if test -s confdefs.h; then cat <<\_ASBOX ## ----------- ## ## confdefs.h. ## ## ----------- ## _ASBOX echo cat confdefs.h echo fi test "$ac_signal" != 0 && $as_echo "$as_me: caught signal $ac_signal" $as_echo "$as_me: exit $exit_status" } >&5 rm -f core *.core core.conftest.* && rm -f -r conftest* confdefs* conf$$* $ac_clean_files && exit $exit_status ' 0 for ac_signal in 1 2 13 15; do trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal done ac_signal=0 # confdefs.h avoids OS command line length limits that DEFS can exceed. rm -f -r conftest* confdefs.h # Predefined preprocessor variables. cat >>confdefs.h <<_ACEOF #define PACKAGE_NAME "$PACKAGE_NAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_TARNAME "$PACKAGE_TARNAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_VERSION "$PACKAGE_VERSION" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_STRING "$PACKAGE_STRING" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" _ACEOF # Let the site file select an alternate cache file if it wants to. # Prefer an explicitly selected file to automatically selected ones. ac_site_file1=NONE ac_site_file2=NONE if test -n "$CONFIG_SITE"; then ac_site_file1=$CONFIG_SITE elif test "x$prefix" != xNONE; then ac_site_file1=$prefix/share/config.site ac_site_file2=$prefix/etc/config.site else ac_site_file1=$ac_default_prefix/share/config.site ac_site_file2=$ac_default_prefix/etc/config.site fi for ac_site_file in "$ac_site_file1" "$ac_site_file2" do test "x$ac_site_file" = xNONE && continue if test -r "$ac_site_file"; then { $as_echo "$as_me:$LINENO: loading site script $ac_site_file" >&5 $as_echo "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 . "$ac_site_file" fi done if test -r "$cache_file"; then # Some versions of bash will fail to source /dev/null (special # files actually), so we avoid doing that. if test -f "$cache_file"; then { $as_echo "$as_me:$LINENO: loading cache $cache_file" >&5 $as_echo "$as_me: loading cache $cache_file" >&6;} case $cache_file in [\\/]* | ?:[\\/]* ) . "$cache_file";; *) . "./$cache_file";; esac fi else { $as_echo "$as_me:$LINENO: creating cache $cache_file" >&5 $as_echo "$as_me: creating cache $cache_file" >&6;} >$cache_file fi # Check that the precious variables saved in the cache have kept the same # value. ac_cache_corrupted=false for ac_var in $ac_precious_vars; do eval ac_old_set=\$ac_cv_env_${ac_var}_set eval ac_new_set=\$ac_env_${ac_var}_set eval ac_old_val=\$ac_cv_env_${ac_var}_value eval ac_new_val=\$ac_env_${ac_var}_value case $ac_old_set,$ac_new_set in set,) { $as_echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 $as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} ac_cache_corrupted=: ;; ,set) { $as_echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5 $as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} ac_cache_corrupted=: ;; ,);; *) if test "x$ac_old_val" != "x$ac_new_val"; then # differences in whitespace do not lead to failure. ac_old_val_w=`echo x $ac_old_val` ac_new_val_w=`echo x $ac_new_val` if test "$ac_old_val_w" != "$ac_new_val_w"; then { $as_echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5 $as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} ac_cache_corrupted=: else { $as_echo "$as_me:$LINENO: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 $as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} eval $ac_var=\$ac_old_val fi { $as_echo "$as_me:$LINENO: former value: \`$ac_old_val'" >&5 $as_echo "$as_me: former value: \`$ac_old_val'" >&2;} { $as_echo "$as_me:$LINENO: current value: \`$ac_new_val'" >&5 $as_echo "$as_me: current value: \`$ac_new_val'" >&2;} fi;; esac # Pass precious variables to config.status. if test "$ac_new_set" = set; then case $ac_new_val in *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; *) ac_arg=$ac_var=$ac_new_val ;; esac case " $ac_configure_args " in *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. *) ac_configure_args="$ac_configure_args '$ac_arg'" ;; esac fi done if $ac_cache_corrupted; then { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} { $as_echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5 $as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} { { $as_echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5 $as_echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;} { (exit 1); exit 1; }; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu # Package information MYLIBVER=3 MYLIBREV=23 MYPROTVER="0.91" # Targets MYHEADERFILES="ttutil.h tculog.h tcrdb.h" MYLIBRARYFILES="libtokyotyrant.a" MYLIBOBJFILES="ttutil.o tculog.o tcrdb.o myconf.o" MYCOMMANDFILES="ttserver ttulmgr ttultest tcrtest tcrmttest tcrmgr" MYINITFILES="ttservctl" MYMAN1FILES="ttserver.1 ttulmgr.1 ttultest.1 tcrtest.1 tcrmttest.1 tcrmgr.1" MYMAN3FILES="ttutil.3 tculog.3 tcrdb.3" MYMAN8FILES="ttservctl.8" MYSCREXTFILES="ext" MYDOCUMENTFILES="COPYING ChangeLog THANKS doc" MYPCFILES="tokyotyrant.pc" # Building flags MYCFLAGS="-std=c99 -Wall -fPIC -fsigned-char -O2" MYCPPFLAGS="-I. -I\$(INCLUDEDIR) -I$HOME/include -I/usr/local/include" MYCPPFLAGS="$MYCPPFLAGS -DNDEBUG -D_GNU_SOURCE=1 -D_REENTRANT -D__EXTENSIONS__" MYLDFLAGS="-L. -L\$(LIBDIR) -L$HOME/lib -L/usr/local/lib" MYCMDLDFLAGS="" MYRUNPATH="\$(LIBDIR)" MYLDLIBPATHENV="LD_LIBRARY_PATH" MYPOSTCMD="true" # Building paths PATH="$PATH:$HOME/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin" PATH="$PATH:/opt/SUNWspro/bin:/usr/ccs/bin:/usr/xpg4/bin:/usr/xpg6/bin:/usr/ucb" CPATH="$HOME/include:/usr/local/include:$CPATH" LIBRARY_PATH="$HOME/lib:/usr/local/lib:$LIBRARY_PATH" LD_LIBRARY_PATH="$HOME/lib:/usr/local/lib:$LD_LIBRARY_PATH" PKG_CONFIG_PATH="$HOME/lib/pkgconfig:/usr/local/lib/pkgconfig:$PKG_CONFIG_PATH" export PATH CPATH LIBRARY_PATH LD_LIBRARY_PATH PKG_CONFIG_PATH if type pkg-config > /dev/null 2>&1 then PATH="$PATH:`pkg-config --variable=bindir tokyocabinet`" CPATH="$CPATH:`pkg-config --variable=includedir tokyocabinet`" LIBRARY_PATH="$LIBRARY_PATH:`pkg-config --variable=libdir tokyocabinet`" LD_LIBRARY_PATH="$LD_LIBRARY_PATH:`pkg-config --variable=libdir tokyocabinet`" fi #================================================================ # Options #================================================================ # Internal variables enables="" # Debug mode # Check whether --enable-debug was given. if test "${enable_debug+set}" = set; then enableval=$enable_debug; fi if test "$enable_debug" = "yes" then MYCFLAGS="-std=c99 -Wall -fPIC -pedantic -fsigned-char -g -O0" MYCPPFLAGS="$MYCPPFLAGS -UNDEBUG" MYCMDLDFLAGS="$MYCMDLDFLAGS -static" enables="$enables (debug)" fi # Developping mode # Check whether --enable-devel was given. if test "${enable_devel+set}" = set; then enableval=$enable_devel; fi if test "$enable_devel" = "yes" then MYCFLAGS="-std=c99 -Wall -fPIC -pedantic -fsigned-char -g -O2 -pipe" MYCPPFLAGS="$MYCPPFLAGS -UNDEBUG" enables="$enables (devel)" fi # Profiling mode # Check whether --enable-profile was given. if test "${enable_profile+set}" = set; then enableval=$enable_profile; fi if test "$enable_profile" = "yes" then MYCFLAGS="-std=c99 -Wall -fPIC -pedantic -fsigned-char -g -pg -O2 -pipe" enables="$enables (profile)" fi # Static mode # Check whether --enable-static was given. if test "${enable_static+set}" = set; then enableval=$enable_static; fi if test "$enable_static" = "yes" then MYCMDLDFLAGS="$MYCMDLDFLAGS -static" enables="$enables (static)" fi # Disable shared object # Check whether --enable-shared was given. if test "${enable_shared+set}" = set; then enableval=$enable_shared; fi if test "$enable_shared" = "no" then enables="$enables (no-shared)" fi # Enable Lua extension # Check whether --enable-lua was given. if test "${enable_lua+set}" = set; then enableval=$enable_lua; fi if test "$enable_lua" = "yes" then enables="$enables (lua)" luaver=`lua -e 'v = string.gsub(_VERSION, ".* ", ""); print(v)'` MYCPPFLAGS="$MYCPPFLAGS -I/usr/include/lua$luaver -I/usr/local/include/lua$luaver" MYCPPFLAGS="$MYCPPFLAGS -I/usr/include/lua -I/usr/local/include/lua -D_MYLUA" MYLDFLAGS="$MYLDFLAGS -L/usr/include/lua$luaver -L/usr/local/include/lua$luaver" MYLDFLAGS="$MYLDFLAGS -L/usr/include/lua -L/usr/local/include/lua" CPATH="$CPATH:/usr/include/lua$luaver:/usr/local/include/lua$luaver" CPATH="$CPATH:/usr/include/lua:/usr/local/include/lua" LIBRARY_PATH="$LIBRARY_PATH:/usr/lib/lua$luaver:/usr/local/lib/lua$luaver" LIBRARY_PATH="$LIBRARY_PATH:/usr/lib/lua:/usr/local/lib/lua" LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/include/lua$luaver:/usr/local/include/lua$luaver" LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/include/lua:/usr/local/include/lua" fi # Specify the installation path of Tokyo Cabinet # Check whether --with-tc was given. if test "${with_tc+set}" = set; then withval=$with_tc; fi if test -n "$with_tc" then MYCPPFLAGS="$MYCPPFLAGS -I$with_tc/include" MYLDFLAGS="$MYLDFLAGS -L$with_tc/lib" MYRUNPATH="$MYRUNPATH:$with_tc/lib" CPATH="$CPATH:$with_tc/include" LIBRARY_PATH="$LIBRARY_PATH:$with_tc/lib" LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$with_tc/lib" fi # Specify the installation path of ZLIB # Check whether --with-zlib was given. if test "${with_zlib+set}" = set; then withval=$with_zlib; fi if test -n "$with_zlib" then MYCPPFLAGS="$MYCPPFLAGS -I$with_zlib/include" MYLDFLAGS="$MYLDFLAGS -L$with_zlib/lib" MYRUNPATH="$MYRUNPATH:$with_zlib/lib" CPATH="$CPATH:$with_zlib/include" LIBRARY_PATH="$LIBRARY_PATH:$with_zlib/lib" LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$with_zlib/lib" fi # Specify the installation path of BZIP2 # Check whether --with-bzip was given. if test "${with_bzip+set}" = set; then withval=$with_bzip; fi if test -n "$with_bzip" then MYCPPFLAGS="$MYCPPFLAGS -I$with_bzip/include" MYLDFLAGS="$MYLDFLAGS -L$with_bzip/lib" MYRUNPATH="$MYRUNPATH:$with_bzip/lib" CPATH="$CPATH:$with_bzip/include" LIBRARY_PATH="$LIBRARY_PATH:$with_bzip/lib" LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$with_bzip/lib" fi # Specify the installation path of Lua # Check whether --with-lua was given. if test "${with_lua+set}" = set; then withval=$with_lua; fi if test -n "$with_lua" then MYCPPFLAGS="$MYCPPFLAGS -I$with_lua/include" MYLDFLAGS="$MYLDFLAGS -L$with_lua/lib" MYRUNPATH="$MYRUNPATH:$with_lua/lib" CPATH="$CPATH:$with_lua/include" LIBRARY_PATH="$LIBRARY_PATH:$with_lua/lib" LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$with_lua/lib" fi # Messages printf '#================================================================\n' printf '# Configuring Tokyo Tyrant version %s%s.\n' "$PACKAGE_VERSION" "$enables" printf '#================================================================\n' #================================================================ # Checking Commands and Libraries #================================================================ # C compiler ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. set dummy ${ac_tool_prefix}gcc; ac_word=$2 { $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_CC+set}" = set; then $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_CC="${ac_tool_prefix}gcc" $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:$LINENO: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:$LINENO: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 { $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_ac_ct_CC+set}" = set; then $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_CC="gcc" $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:$LINENO: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi else CC="$ac_cv_prog_CC" fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. set dummy ${ac_tool_prefix}cc; ac_word=$2 { $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_CC+set}" = set; then $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_CC="${ac_tool_prefix}cc" $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:$LINENO: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:$LINENO: result: no" >&5 $as_echo "no" >&6; } fi fi fi if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 { $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_CC+set}" = set; then $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else ac_prog_rejected=no as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue fi ac_cv_prog_CC="cc" $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS if test $ac_prog_rejected = yes; then # We found a bogon in the path, so make sure we never use it. set dummy $ac_cv_prog_CC shift if test $# != 0; then # We chose a different compiler from the bogus one. # However, it has the same basename, so the bogon will be chosen # first if we set CC to just the basename; use the full file name. shift ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" fi fi fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:$LINENO: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:$LINENO: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then for ac_prog in cl.exe do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_CC+set}" = set; then $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:$LINENO: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:$LINENO: result: no" >&5 $as_echo "no" >&6; } fi test -n "$CC" && break done fi if test -z "$CC"; then ac_ct_CC=$CC for ac_prog in cl.exe do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_ac_ct_CC+set}" = set; then $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_CC="$ac_prog" $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:$LINENO: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_CC" && break done if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi fi fi test -z "$CC" && { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} { { $as_echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH See \`config.log' for more details." >&5 $as_echo "$as_me: error: no acceptable C compiler found in \$PATH See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; }; } # Provide some information about the compiler. $as_echo "$as_me:$LINENO: checking for C compiler version" >&5 set X $ac_compile ac_compiler=$2 { (ac_try="$ac_compiler --version >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" $as_echo "$ac_try_echo") >&5 (eval "$ac_compiler --version >&5") 2>&5 ac_status=$? $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } { (ac_try="$ac_compiler -v >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" $as_echo "$ac_try_echo") >&5 (eval "$ac_compiler -v >&5") 2>&5 ac_status=$? $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } { (ac_try="$ac_compiler -V >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" $as_echo "$ac_try_echo") >&5 (eval "$ac_compiler -V >&5") 2>&5 ac_status=$? $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { ; return 0; } _ACEOF ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" # Try to create an executable without -o first, disregard a.out. # It will help us diagnose broken compilers, and finding out an intuition # of exeext. { $as_echo "$as_me:$LINENO: checking for C compiler default output file name" >&5 $as_echo_n "checking for C compiler default output file name... " >&6; } ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` # The possible output files: ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" ac_rmfiles= for ac_file in $ac_files do case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; * ) ac_rmfiles="$ac_rmfiles $ac_file";; esac done rm -f $ac_rmfiles if { (ac_try="$ac_link_default" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" $as_echo "$ac_try_echo") >&5 (eval "$ac_link_default") 2>&5 ac_status=$? $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; then # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. # So ignore a value of `no', otherwise this would lead to `EXEEXT = no' # in a Makefile. We should not override ac_cv_exeext if it was cached, # so that the user can short-circuit this test for compilers unknown to # Autoconf. for ac_file in $ac_files '' do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; [ab].out ) # We found the default executable, but exeext='' is most # certainly right. break;; *.* ) if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; then :; else ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` fi # We set ac_cv_exeext here because the later test for it is not # safe: cross compilers may not add the suffix if given an `-o' # argument, so we may need to know it at that point already. # Even if this section looks crufty: it has the advantage of # actually working. break;; * ) break;; esac done test "$ac_cv_exeext" = no && ac_cv_exeext= else ac_file='' fi { $as_echo "$as_me:$LINENO: result: $ac_file" >&5 $as_echo "$ac_file" >&6; } if test -z "$ac_file"; then $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} { { $as_echo "$as_me:$LINENO: error: C compiler cannot create executables See \`config.log' for more details." >&5 $as_echo "$as_me: error: C compiler cannot create executables See \`config.log' for more details." >&2;} { (exit 77); exit 77; }; }; } fi ac_exeext=$ac_cv_exeext # Check that the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. { $as_echo "$as_me:$LINENO: checking whether the C compiler works" >&5 $as_echo_n "checking whether the C compiler works... " >&6; } # FIXME: These cross compiler hacks should be removed for Autoconf 3.0 # If not cross compiling, check that we can run a simple program. if test "$cross_compiling" != yes; then if { ac_try='./$ac_file' { (case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" $as_echo "$ac_try_echo") >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then cross_compiling=no else if test "$cross_compiling" = maybe; then cross_compiling=yes else { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} { { $as_echo "$as_me:$LINENO: error: cannot run C compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details." >&5 $as_echo "$as_me: error: cannot run C compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; }; } fi fi fi { $as_echo "$as_me:$LINENO: result: yes" >&5 $as_echo "yes" >&6; } rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out ac_clean_files=$ac_clean_files_save # Check that the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. { $as_echo "$as_me:$LINENO: checking whether we are cross compiling" >&5 $as_echo_n "checking whether we are cross compiling... " >&6; } { $as_echo "$as_me:$LINENO: result: $cross_compiling" >&5 $as_echo "$cross_compiling" >&6; } { $as_echo "$as_me:$LINENO: checking for suffix of executables" >&5 $as_echo_n "checking for suffix of executables... " >&6; } if { (ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" $as_echo "$ac_try_echo") >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; then # If both `conftest.exe' and `conftest' are `present' (well, observable) # catch `conftest.exe'. For instance with Cygwin, `ls conftest' will # work properly (i.e., refer to `conftest.exe'), while it won't with # `rm'. for ac_file in conftest.exe conftest conftest.*; do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` break;; * ) break;; esac done else { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} { { $as_echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link See \`config.log' for more details." >&5 $as_echo "$as_me: error: cannot compute suffix of executables: cannot compile and link See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; }; } fi rm -f conftest$ac_cv_exeext { $as_echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5 $as_echo "$ac_cv_exeext" >&6; } rm -f conftest.$ac_ext EXEEXT=$ac_cv_exeext ac_exeext=$EXEEXT { $as_echo "$as_me:$LINENO: checking for suffix of object files" >&5 $as_echo_n "checking for suffix of object files... " >&6; } if test "${ac_cv_objext+set}" = set; then $as_echo_n "(cached) " >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.o conftest.obj if { (ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" $as_echo "$ac_try_echo") >&5 (eval "$ac_compile") 2>&5 ac_status=$? $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; then for ac_file in conftest.o conftest.obj conftest.*; do test -f "$ac_file" || continue; case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` break;; esac done else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} { { $as_echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile See \`config.log' for more details." >&5 $as_echo "$as_me: error: cannot compute suffix of object files: cannot compile See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; }; } fi rm -f conftest.$ac_cv_objext conftest.$ac_ext fi { $as_echo "$as_me:$LINENO: result: $ac_cv_objext" >&5 $as_echo "$ac_cv_objext" >&6; } OBJEXT=$ac_cv_objext ac_objext=$OBJEXT { $as_echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5 $as_echo_n "checking whether we are using the GNU C compiler... " >&6; } if test "${ac_cv_c_compiler_gnu+set}" = set; then $as_echo_n "(cached) " >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" $as_echo "$ac_try_echo") >&5 (eval "$ac_compile") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then ac_compiler_gnu=yes else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu fi { $as_echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5 $as_echo "$ac_cv_c_compiler_gnu" >&6; } if test $ac_compiler_gnu = yes; then GCC=yes else GCC= fi ac_test_CFLAGS=${CFLAGS+set} ac_save_CFLAGS=$CFLAGS { $as_echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5 $as_echo_n "checking whether $CC accepts -g... " >&6; } if test "${ac_cv_prog_cc_g+set}" = set; then $as_echo_n "(cached) " >&6 else ac_save_c_werror_flag=$ac_c_werror_flag ac_c_werror_flag=yes ac_cv_prog_cc_g=no CFLAGS="-g" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" $as_echo "$ac_try_echo") >&5 (eval "$ac_compile") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then ac_cv_prog_cc_g=yes else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 CFLAGS="" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" $as_echo "$ac_try_echo") >&5 (eval "$ac_compile") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then : else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_c_werror_flag=$ac_save_c_werror_flag CFLAGS="-g" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" $as_echo "$ac_try_echo") >&5 (eval "$ac_compile") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then ac_cv_prog_cc_g=yes else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_c_werror_flag=$ac_save_c_werror_flag fi { $as_echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5 $as_echo "$ac_cv_prog_cc_g" >&6; } if test "$ac_test_CFLAGS" = set; then CFLAGS=$ac_save_CFLAGS elif test $ac_cv_prog_cc_g = yes; then if test "$GCC" = yes; then CFLAGS="-g -O2" else CFLAGS="-g" fi else if test "$GCC" = yes; then CFLAGS="-O2" else CFLAGS= fi fi { $as_echo "$as_me:$LINENO: checking for $CC option to accept ISO C89" >&5 $as_echo_n "checking for $CC option to accept ISO C89... " >&6; } if test "${ac_cv_prog_cc_c89+set}" = set; then $as_echo_n "(cached) " >&6 else ac_cv_prog_cc_c89=no ac_save_CC=$CC cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #include #include #include /* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ struct buf { int x; }; FILE * (*rcsopen) (struct buf *, struct stat *, int); static char *e (p, i) char **p; int i; { return p[i]; } static char *f (char * (*g) (char **, int), char **p, ...) { char *s; va_list v; va_start (v,p); s = g (p, va_arg (v,int)); va_end (v); return s; } /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has function prototypes and stuff, but not '\xHH' hex character constants. These don't provoke an error unfortunately, instead are silently treated as 'x'. The following induces an error, until -std is added to get proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an array size at least. It's necessary to write '\x00'==0 to get something that's true only with -std. */ int osf4_cc_array ['\x00' == 0 ? 1 : -1]; /* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters inside strings and character constants. */ #define FOO(x) 'x' int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; int test (int i, double x); struct s1 {int (*f) (int a);}; struct s2 {int (*f) (double a);}; int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); int argc; char **argv; int main () { return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; ; return 0; } _ACEOF for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" do CC="$ac_save_CC $ac_arg" rm -f conftest.$ac_objext if { (ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" $as_echo "$ac_try_echo") >&5 (eval "$ac_compile") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then ac_cv_prog_cc_c89=$ac_arg else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f core conftest.err conftest.$ac_objext test "x$ac_cv_prog_cc_c89" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi # AC_CACHE_VAL case "x$ac_cv_prog_cc_c89" in x) { $as_echo "$as_me:$LINENO: result: none needed" >&5 $as_echo "none needed" >&6; } ;; xno) { $as_echo "$as_me:$LINENO: result: unsupported" >&5 $as_echo "unsupported" >&6; } ;; *) CC="$CC $ac_cv_prog_cc_c89" { $as_echo "$as_me:$LINENO: result: $ac_cv_prog_cc_c89" >&5 $as_echo "$ac_cv_prog_cc_c89" >&6; } ;; esac ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu # Reset variables if test "$GCC" != "yes" then { $as_echo "$as_me:$LINENO: WARNING: another compiler except for GCC was detected" >&5 $as_echo "$as_me: WARNING: another compiler except for GCC was detected" >&2;} MYCFLAGS="" fi test -n "$CFLAGS" && MYCFLAGS="$CFLAGS $MYCFLAGS" test -n "$CPPFLAGS" && MYCPPFLAGS="$CPPFLAGS $MYCPPFLAGS" test -n "$LDFLAGS" && MYLDFLAGS="$LDFLAGS $MYLDFLAGS" # Byte order ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:$LINENO: checking how to run the C preprocessor" >&5 $as_echo_n "checking how to run the C preprocessor... " >&6; } # On Suns, sometimes $CPP names a directory. if test -n "$CPP" && test -d "$CPP"; then CPP= fi if test -z "$CPP"; then if test "${ac_cv_prog_CPP+set}" = set; then $as_echo_n "(cached) " >&6 else # Double quotes because CPP needs to be expanded for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" do ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if { (ac_try="$ac_cpp conftest.$ac_ext" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" $as_echo "$ac_try_echo") >&5 (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null && { test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || test ! -s conftest.err }; then : else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 # Broken: fails on valid input. continue fi rm -f conftest.err conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include _ACEOF if { (ac_try="$ac_cpp conftest.$ac_ext" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" $as_echo "$ac_try_echo") >&5 (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null && { test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || test ! -s conftest.err }; then # Broken: success on invalid input. continue else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.err conftest.$ac_ext if $ac_preproc_ok; then break fi done ac_cv_prog_CPP=$CPP fi CPP=$ac_cv_prog_CPP else ac_cv_prog_CPP=$CPP fi { $as_echo "$as_me:$LINENO: result: $CPP" >&5 $as_echo "$CPP" >&6; } ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if { (ac_try="$ac_cpp conftest.$ac_ext" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" $as_echo "$ac_try_echo") >&5 (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null && { test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || test ! -s conftest.err }; then : else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 # Broken: fails on valid input. continue fi rm -f conftest.err conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include _ACEOF if { (ac_try="$ac_cpp conftest.$ac_ext" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" $as_echo "$ac_try_echo") >&5 (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null && { test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || test ! -s conftest.err }; then # Broken: success on invalid input. continue else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.err conftest.$ac_ext if $ac_preproc_ok; then : else { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} { { $as_echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check See \`config.log' for more details." >&5 $as_echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; }; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:$LINENO: checking for grep that handles long lines and -e" >&5 $as_echo_n "checking for grep that handles long lines and -e... " >&6; } if test "${ac_cv_path_GREP+set}" = set; then $as_echo_n "(cached) " >&6 else if test -z "$GREP"; then ac_path_GREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in grep ggrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" { test -f "$ac_path_GREP" && $as_test_x "$ac_path_GREP"; } || continue # Check for GNU ac_path_GREP and select it if it is found. # Check for GNU $ac_path_GREP case `"$ac_path_GREP" --version 2>&1` in *GNU*) ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'GREP' >> "conftest.nl" "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break ac_count=`expr $ac_count + 1` if test $ac_count -gt ${ac_path_GREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_GREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_GREP"; then { { $as_echo "$as_me:$LINENO: error: no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&5 $as_echo "$as_me: error: no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&2;} { (exit 1); exit 1; }; } fi else ac_cv_path_GREP=$GREP fi fi { $as_echo "$as_me:$LINENO: result: $ac_cv_path_GREP" >&5 $as_echo "$ac_cv_path_GREP" >&6; } GREP="$ac_cv_path_GREP" { $as_echo "$as_me:$LINENO: checking for egrep" >&5 $as_echo_n "checking for egrep... " >&6; } if test "${ac_cv_path_EGREP+set}" = set; then $as_echo_n "(cached) " >&6 else if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 then ac_cv_path_EGREP="$GREP -E" else if test -z "$EGREP"; then ac_path_EGREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in egrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" { test -f "$ac_path_EGREP" && $as_test_x "$ac_path_EGREP"; } || continue # Check for GNU ac_path_EGREP and select it if it is found. # Check for GNU $ac_path_EGREP case `"$ac_path_EGREP" --version 2>&1` in *GNU*) ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'EGREP' >> "conftest.nl" "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break ac_count=`expr $ac_count + 1` if test $ac_count -gt ${ac_path_EGREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_EGREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_EGREP"; then { { $as_echo "$as_me:$LINENO: error: no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&5 $as_echo "$as_me: error: no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&2;} { (exit 1); exit 1; }; } fi else ac_cv_path_EGREP=$EGREP fi fi fi { $as_echo "$as_me:$LINENO: result: $ac_cv_path_EGREP" >&5 $as_echo "$ac_cv_path_EGREP" >&6; } EGREP="$ac_cv_path_EGREP" { $as_echo "$as_me:$LINENO: checking for ANSI C header files" >&5 $as_echo_n "checking for ANSI C header files... " >&6; } if test "${ac_cv_header_stdc+set}" = set; then $as_echo_n "(cached) " >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #include #include #include int main () { ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" $as_echo "$ac_try_echo") >&5 (eval "$ac_compile") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then ac_cv_header_stdc=yes else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_header_stdc=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext if test $ac_cv_header_stdc = yes; then # SunOS 4.x string.h does not declare mem*, contrary to ANSI. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "memchr" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "free" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. if test "$cross_compiling" = yes; then : else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #include #if ((' ' & 0x0FF) == 0x020) # define ISLOWER(c) ('a' <= (c) && (c) <= 'z') # define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) #else # define ISLOWER(c) \ (('a' <= (c) && (c) <= 'i') \ || ('j' <= (c) && (c) <= 'r') \ || ('s' <= (c) && (c) <= 'z')) # define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) #endif #define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) int main () { int i; for (i = 0; i < 256; i++) if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) return 2; return 0; } _ACEOF rm -f conftest$ac_exeext if { (ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" $as_echo "$ac_try_echo") >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='./conftest$ac_exeext' { (case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" $as_echo "$ac_try_echo") >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then : else $as_echo "$as_me: program exited with status $ac_status" >&5 $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ( exit $ac_status ) ac_cv_header_stdc=no fi rm -rf conftest.dSYM rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext fi fi fi { $as_echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5 $as_echo "$ac_cv_header_stdc" >&6; } if test $ac_cv_header_stdc = yes; then cat >>confdefs.h <<\_ACEOF #define STDC_HEADERS 1 _ACEOF fi # On IRIX 5.3, sys/types and inttypes.h are conflicting. for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ inttypes.h stdint.h unistd.h do as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5 $as_echo_n "checking for $ac_header... " >&6; } if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then $as_echo_n "(cached) " >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default #include <$ac_header> _ACEOF rm -f conftest.$ac_objext if { (ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" $as_echo "$ac_try_echo") >&5 (eval "$ac_compile") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then eval "$as_ac_Header=yes" else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 eval "$as_ac_Header=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi ac_res=`eval 'as_val=${'$as_ac_Header'} $as_echo "$as_val"'` { $as_echo "$as_me:$LINENO: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } as_val=`eval 'as_val=${'$as_ac_Header'} $as_echo "$as_val"'` if test "x$as_val" = x""yes; then cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done { $as_echo "$as_me:$LINENO: checking whether byte ordering is bigendian" >&5 $as_echo_n "checking whether byte ordering is bigendian... " >&6; } if test "${ac_cv_c_bigendian+set}" = set; then $as_echo_n "(cached) " >&6 else ac_cv_c_bigendian=unknown # See if we're dealing with a universal compiler. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #ifndef __APPLE_CC__ not a universal capable compiler #endif typedef int dummy; _ACEOF rm -f conftest.$ac_objext if { (ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" $as_echo "$ac_try_echo") >&5 (eval "$ac_compile") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then # Check for potential -arch flags. It is not universal unless # there are some -arch flags. Note that *ppc* also matches # ppc64. This check is also rather less than ideal. case "${CC} ${CFLAGS} ${CPPFLAGS} ${LDFLAGS}" in #( *-arch*ppc*|*-arch*i386*|*-arch*x86_64*) ac_cv_c_bigendian=universal;; esac else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext if test $ac_cv_c_bigendian = unknown; then # See if sys/param.h defines the BYTE_ORDER macro. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #include int main () { #if ! (defined BYTE_ORDER && defined BIG_ENDIAN \ && defined LITTLE_ENDIAN && BYTE_ORDER && BIG_ENDIAN \ && LITTLE_ENDIAN) bogus endian macros #endif ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" $as_echo "$ac_try_echo") >&5 (eval "$ac_compile") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then # It does; now see whether it defined to BIG_ENDIAN or not. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #include int main () { #if BYTE_ORDER != BIG_ENDIAN not big endian #endif ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" $as_echo "$ac_try_echo") >&5 (eval "$ac_compile") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then ac_cv_c_bigendian=yes else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_c_bigendian=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi if test $ac_cv_c_bigendian = unknown; then # See if defines _LITTLE_ENDIAN or _BIG_ENDIAN (e.g., Solaris). cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include int main () { #if ! (defined _LITTLE_ENDIAN || defined _BIG_ENDIAN) bogus endian macros #endif ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" $as_echo "$ac_try_echo") >&5 (eval "$ac_compile") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then # It does; now see whether it defined to _BIG_ENDIAN or not. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include int main () { #ifndef _BIG_ENDIAN not big endian #endif ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" $as_echo "$ac_try_echo") >&5 (eval "$ac_compile") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then ac_cv_c_bigendian=yes else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_c_bigendian=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi if test $ac_cv_c_bigendian = unknown; then # Compile a test program. if test "$cross_compiling" = yes; then # Try to guess by grepping values from an object file. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ short int ascii_mm[] = { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 }; short int ascii_ii[] = { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 }; int use_ascii (int i) { return ascii_mm[i] + ascii_ii[i]; } short int ebcdic_ii[] = { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 }; short int ebcdic_mm[] = { 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 }; int use_ebcdic (int i) { return ebcdic_mm[i] + ebcdic_ii[i]; } extern int foo; int main () { return use_ascii (foo) == use_ebcdic (foo); ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" $as_echo "$ac_try_echo") >&5 (eval "$ac_compile") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then if grep BIGenDianSyS conftest.$ac_objext >/dev/null; then ac_cv_c_bigendian=yes fi if grep LiTTleEnDian conftest.$ac_objext >/dev/null ; then if test "$ac_cv_c_bigendian" = unknown; then ac_cv_c_bigendian=no else # finding both strings is unlikely to happen, but who knows? ac_cv_c_bigendian=unknown fi fi else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default int main () { /* Are we little or big endian? From Harbison&Steele. */ union { long int l; char c[sizeof (long int)]; } u; u.l = 1; return u.c[sizeof (long int) - 1] == 1; ; return 0; } _ACEOF rm -f conftest$ac_exeext if { (ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" $as_echo "$ac_try_echo") >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='./conftest$ac_exeext' { (case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" $as_echo "$ac_try_echo") >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_c_bigendian=no else $as_echo "$as_me: program exited with status $ac_status" >&5 $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ( exit $ac_status ) ac_cv_c_bigendian=yes fi rm -rf conftest.dSYM rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext fi fi fi { $as_echo "$as_me:$LINENO: result: $ac_cv_c_bigendian" >&5 $as_echo "$ac_cv_c_bigendian" >&6; } case $ac_cv_c_bigendian in #( yes) MYCPPFLAGS="$MYCPPFLAGS -D_MYBIGEND";; #( no) ;; #( universal) cat >>confdefs.h <<\_ACEOF #define AC_APPLE_UNIVERSAL_BUILD 1 _ACEOF ;; #( *) { { $as_echo "$as_me:$LINENO: error: unknown endianness presetting ac_cv_c_bigendian=no (or yes) will help" >&5 $as_echo "$as_me: error: unknown endianness presetting ac_cv_c_bigendian=no (or yes) will help" >&2;} { (exit 1); exit 1; }; } ;; esac # Underlying libraries { $as_echo "$as_me:$LINENO: checking for main in -lc" >&5 $as_echo_n "checking for main in -lc... " >&6; } if test "${ac_cv_lib_c_main+set}" = set; then $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lc $LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { return main (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" $as_echo "$ac_try_echo") >&5 (eval "$ac_link") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || $as_test_x conftest$ac_exeext }; then ac_cv_lib_c_main=yes else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_lib_c_main=no fi rm -rf conftest.dSYM rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:$LINENO: result: $ac_cv_lib_c_main" >&5 $as_echo "$ac_cv_lib_c_main" >&6; } if test "x$ac_cv_lib_c_main" = x""yes; then cat >>confdefs.h <<_ACEOF #define HAVE_LIBC 1 _ACEOF LIBS="-lc $LIBS" fi { $as_echo "$as_me:$LINENO: checking for main in -lm" >&5 $as_echo_n "checking for main in -lm... " >&6; } if test "${ac_cv_lib_m_main+set}" = set; then $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lm $LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { return main (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" $as_echo "$ac_try_echo") >&5 (eval "$ac_link") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || $as_test_x conftest$ac_exeext }; then ac_cv_lib_m_main=yes else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_lib_m_main=no fi rm -rf conftest.dSYM rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:$LINENO: result: $ac_cv_lib_m_main" >&5 $as_echo "$ac_cv_lib_m_main" >&6; } if test "x$ac_cv_lib_m_main" = x""yes; then cat >>confdefs.h <<_ACEOF #define HAVE_LIBM 1 _ACEOF LIBS="-lm $LIBS" fi { $as_echo "$as_me:$LINENO: checking for main in -lpthread" >&5 $as_echo_n "checking for main in -lpthread... " >&6; } if test "${ac_cv_lib_pthread_main+set}" = set; then $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lpthread $LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { return main (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" $as_echo "$ac_try_echo") >&5 (eval "$ac_link") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || $as_test_x conftest$ac_exeext }; then ac_cv_lib_pthread_main=yes else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_lib_pthread_main=no fi rm -rf conftest.dSYM rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:$LINENO: result: $ac_cv_lib_pthread_main" >&5 $as_echo "$ac_cv_lib_pthread_main" >&6; } if test "x$ac_cv_lib_pthread_main" = x""yes; then cat >>confdefs.h <<_ACEOF #define HAVE_LIBPTHREAD 1 _ACEOF LIBS="-lpthread $LIBS" fi { $as_echo "$as_me:$LINENO: checking for main in -lrt" >&5 $as_echo_n "checking for main in -lrt... " >&6; } if test "${ac_cv_lib_rt_main+set}" = set; then $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lrt $LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { return main (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" $as_echo "$ac_try_echo") >&5 (eval "$ac_link") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || $as_test_x conftest$ac_exeext }; then ac_cv_lib_rt_main=yes else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_lib_rt_main=no fi rm -rf conftest.dSYM rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:$LINENO: result: $ac_cv_lib_rt_main" >&5 $as_echo "$ac_cv_lib_rt_main" >&6; } if test "x$ac_cv_lib_rt_main" = x""yes; then cat >>confdefs.h <<_ACEOF #define HAVE_LIBRT 1 _ACEOF LIBS="-lrt $LIBS" fi { $as_echo "$as_me:$LINENO: checking for main in -ldl" >&5 $as_echo_n "checking for main in -ldl... " >&6; } if test "${ac_cv_lib_dl_main+set}" = set; then $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-ldl $LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { return main (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" $as_echo "$ac_try_echo") >&5 (eval "$ac_link") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || $as_test_x conftest$ac_exeext }; then ac_cv_lib_dl_main=yes else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_lib_dl_main=no fi rm -rf conftest.dSYM rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:$LINENO: result: $ac_cv_lib_dl_main" >&5 $as_echo "$ac_cv_lib_dl_main" >&6; } if test "x$ac_cv_lib_dl_main" = x""yes; then cat >>confdefs.h <<_ACEOF #define HAVE_LIBDL 1 _ACEOF LIBS="-ldl $LIBS" fi { $as_echo "$as_me:$LINENO: checking for main in -lnsl" >&5 $as_echo_n "checking for main in -lnsl... " >&6; } if test "${ac_cv_lib_nsl_main+set}" = set; then $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lnsl $LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { return main (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" $as_echo "$ac_try_echo") >&5 (eval "$ac_link") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || $as_test_x conftest$ac_exeext }; then ac_cv_lib_nsl_main=yes else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_lib_nsl_main=no fi rm -rf conftest.dSYM rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:$LINENO: result: $ac_cv_lib_nsl_main" >&5 $as_echo "$ac_cv_lib_nsl_main" >&6; } if test "x$ac_cv_lib_nsl_main" = x""yes; then cat >>confdefs.h <<_ACEOF #define HAVE_LIBNSL 1 _ACEOF LIBS="-lnsl $LIBS" fi { $as_echo "$as_me:$LINENO: checking for main in -lsocket" >&5 $as_echo_n "checking for main in -lsocket... " >&6; } if test "${ac_cv_lib_socket_main+set}" = set; then $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lsocket $LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { return main (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" $as_echo "$ac_try_echo") >&5 (eval "$ac_link") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || $as_test_x conftest$ac_exeext }; then ac_cv_lib_socket_main=yes else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_lib_socket_main=no fi rm -rf conftest.dSYM rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:$LINENO: result: $ac_cv_lib_socket_main" >&5 $as_echo "$ac_cv_lib_socket_main" >&6; } if test "x$ac_cv_lib_socket_main" = x""yes; then cat >>confdefs.h <<_ACEOF #define HAVE_LIBSOCKET 1 _ACEOF LIBS="-lsocket $LIBS" fi { $as_echo "$as_me:$LINENO: checking for main in -lresolv" >&5 $as_echo_n "checking for main in -lresolv... " >&6; } if test "${ac_cv_lib_resolv_main+set}" = set; then $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lresolv $LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { return main (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" $as_echo "$ac_try_echo") >&5 (eval "$ac_link") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || $as_test_x conftest$ac_exeext }; then ac_cv_lib_resolv_main=yes else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_lib_resolv_main=no fi rm -rf conftest.dSYM rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:$LINENO: result: $ac_cv_lib_resolv_main" >&5 $as_echo "$ac_cv_lib_resolv_main" >&6; } if test "x$ac_cv_lib_resolv_main" = x""yes; then cat >>confdefs.h <<_ACEOF #define HAVE_LIBRESOLV 1 _ACEOF LIBS="-lresolv $LIBS" fi { $as_echo "$as_me:$LINENO: checking for main in -lz" >&5 $as_echo_n "checking for main in -lz... " >&6; } if test "${ac_cv_lib_z_main+set}" = set; then $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lz $LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { return main (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" $as_echo "$ac_try_echo") >&5 (eval "$ac_link") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || $as_test_x conftest$ac_exeext }; then ac_cv_lib_z_main=yes else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_lib_z_main=no fi rm -rf conftest.dSYM rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:$LINENO: result: $ac_cv_lib_z_main" >&5 $as_echo "$ac_cv_lib_z_main" >&6; } if test "x$ac_cv_lib_z_main" = x""yes; then cat >>confdefs.h <<_ACEOF #define HAVE_LIBZ 1 _ACEOF LIBS="-lz $LIBS" fi { $as_echo "$as_me:$LINENO: checking for main in -lbz2" >&5 $as_echo_n "checking for main in -lbz2... " >&6; } if test "${ac_cv_lib_bz2_main+set}" = set; then $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lbz2 $LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { return main (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" $as_echo "$ac_try_echo") >&5 (eval "$ac_link") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || $as_test_x conftest$ac_exeext }; then ac_cv_lib_bz2_main=yes else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_lib_bz2_main=no fi rm -rf conftest.dSYM rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:$LINENO: result: $ac_cv_lib_bz2_main" >&5 $as_echo "$ac_cv_lib_bz2_main" >&6; } if test "x$ac_cv_lib_bz2_main" = x""yes; then cat >>confdefs.h <<_ACEOF #define HAVE_LIBBZ2 1 _ACEOF LIBS="-lbz2 $LIBS" fi { $as_echo "$as_me:$LINENO: checking for main in -ltokyocabinet" >&5 $as_echo_n "checking for main in -ltokyocabinet... " >&6; } if test "${ac_cv_lib_tokyocabinet_main+set}" = set; then $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-ltokyocabinet $LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { return main (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" $as_echo "$ac_try_echo") >&5 (eval "$ac_link") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || $as_test_x conftest$ac_exeext }; then ac_cv_lib_tokyocabinet_main=yes else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_lib_tokyocabinet_main=no fi rm -rf conftest.dSYM rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:$LINENO: result: $ac_cv_lib_tokyocabinet_main" >&5 $as_echo "$ac_cv_lib_tokyocabinet_main" >&6; } if test "x$ac_cv_lib_tokyocabinet_main" = x""yes; then cat >>confdefs.h <<_ACEOF #define HAVE_LIBTOKYOCABINET 1 _ACEOF LIBS="-ltokyocabinet $LIBS" fi if test "$enable_lua" = "yes" then { $as_echo "$as_me:$LINENO: checking for main in -llua" >&5 $as_echo_n "checking for main in -llua... " >&6; } if test "${ac_cv_lib_lua_main+set}" = set; then $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-llua $LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { return main (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" $as_echo "$ac_try_echo") >&5 (eval "$ac_link") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || $as_test_x conftest$ac_exeext }; then ac_cv_lib_lua_main=yes else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_lib_lua_main=no fi rm -rf conftest.dSYM rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:$LINENO: result: $ac_cv_lib_lua_main" >&5 $as_echo "$ac_cv_lib_lua_main" >&6; } if test "x$ac_cv_lib_lua_main" = x""yes; then cat >>confdefs.h <<_ACEOF #define HAVE_LIBLUA 1 _ACEOF LIBS="-llua $LIBS" fi as_ac_Lib=`$as_echo "ac_cv_lib_lua$luaver''_main" | $as_tr_sh` { $as_echo "$as_me:$LINENO: checking for main in -llua$luaver" >&5 $as_echo_n "checking for main in -llua$luaver... " >&6; } if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-llua$luaver $LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { return main (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" $as_echo "$ac_try_echo") >&5 (eval "$ac_link") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || $as_test_x conftest$ac_exeext }; then eval "$as_ac_Lib=yes" else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 eval "$as_ac_Lib=no" fi rm -rf conftest.dSYM rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi ac_res=`eval 'as_val=${'$as_ac_Lib'} $as_echo "$as_val"'` { $as_echo "$as_me:$LINENO: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } as_val=`eval 'as_val=${'$as_ac_Lib'} $as_echo "$as_val"'` if test "x$as_val" = x""yes; then cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_LIBlua$luaver" | $as_tr_cpp` 1 _ACEOF LIBS="-llua$luaver $LIBS" fi fi { $as_echo "$as_me:$LINENO: checking for main in -ltokyotyrant" >&5 $as_echo_n "checking for main in -ltokyotyrant... " >&6; } if test "${ac_cv_lib_tokyotyrant_main+set}" = set; then $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-ltokyotyrant $LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { return main (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" $as_echo "$ac_try_echo") >&5 (eval "$ac_link") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || $as_test_x conftest$ac_exeext }; then ac_cv_lib_tokyotyrant_main=yes else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_lib_tokyotyrant_main=no fi rm -rf conftest.dSYM rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:$LINENO: result: $ac_cv_lib_tokyotyrant_main" >&5 $as_echo "$ac_cv_lib_tokyotyrant_main" >&6; } if test "x$ac_cv_lib_tokyotyrant_main" = x""yes; then { $as_echo "$as_me:$LINENO: WARNING: old version of Tokyo Tyrant was detected" >&5 $as_echo "$as_me: WARNING: old version of Tokyo Tyrant was detected" >&2;} fi # Necessary headers if test "${ac_cv_header_stdlib_h+set}" = set; then { $as_echo "$as_me:$LINENO: checking for stdlib.h" >&5 $as_echo_n "checking for stdlib.h... " >&6; } if test "${ac_cv_header_stdlib_h+set}" = set; then $as_echo_n "(cached) " >&6 fi { $as_echo "$as_me:$LINENO: result: $ac_cv_header_stdlib_h" >&5 $as_echo "$ac_cv_header_stdlib_h" >&6; } else # Is the header compilable? { $as_echo "$as_me:$LINENO: checking stdlib.h usability" >&5 $as_echo_n "checking stdlib.h usability... " >&6; } cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default #include _ACEOF rm -f conftest.$ac_objext if { (ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" $as_echo "$ac_try_echo") >&5 (eval "$ac_compile") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then ac_header_compiler=yes else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_compiler=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext { $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 $as_echo "$ac_header_compiler" >&6; } # Is the header present? { $as_echo "$as_me:$LINENO: checking stdlib.h presence" >&5 $as_echo_n "checking stdlib.h presence... " >&6; } cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include _ACEOF if { (ac_try="$ac_cpp conftest.$ac_ext" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" $as_echo "$ac_try_echo") >&5 (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null && { test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || test ! -s conftest.err }; then ac_header_preproc=yes else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_preproc=no fi rm -f conftest.err conftest.$ac_ext { $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 $as_echo "$ac_header_preproc" >&6; } # So? What about this header? case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in yes:no: ) { $as_echo "$as_me:$LINENO: WARNING: stdlib.h: accepted by the compiler, rejected by the preprocessor!" >&5 $as_echo "$as_me: WARNING: stdlib.h: accepted by the compiler, rejected by the preprocessor!" >&2;} { $as_echo "$as_me:$LINENO: WARNING: stdlib.h: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: stdlib.h: proceeding with the compiler's result" >&2;} ac_header_preproc=yes ;; no:yes:* ) { $as_echo "$as_me:$LINENO: WARNING: stdlib.h: present but cannot be compiled" >&5 $as_echo "$as_me: WARNING: stdlib.h: present but cannot be compiled" >&2;} { $as_echo "$as_me:$LINENO: WARNING: stdlib.h: check for missing prerequisite headers?" >&5 $as_echo "$as_me: WARNING: stdlib.h: check for missing prerequisite headers?" >&2;} { $as_echo "$as_me:$LINENO: WARNING: stdlib.h: see the Autoconf documentation" >&5 $as_echo "$as_me: WARNING: stdlib.h: see the Autoconf documentation" >&2;} { $as_echo "$as_me:$LINENO: WARNING: stdlib.h: section \"Present But Cannot Be Compiled\"" >&5 $as_echo "$as_me: WARNING: stdlib.h: section \"Present But Cannot Be Compiled\"" >&2;} { $as_echo "$as_me:$LINENO: WARNING: stdlib.h: proceeding with the preprocessor's result" >&5 $as_echo "$as_me: WARNING: stdlib.h: proceeding with the preprocessor's result" >&2;} { $as_echo "$as_me:$LINENO: WARNING: stdlib.h: in the future, the compiler will take precedence" >&5 $as_echo "$as_me: WARNING: stdlib.h: in the future, the compiler will take precedence" >&2;} ;; esac { $as_echo "$as_me:$LINENO: checking for stdlib.h" >&5 $as_echo_n "checking for stdlib.h... " >&6; } if test "${ac_cv_header_stdlib_h+set}" = set; then $as_echo_n "(cached) " >&6 else ac_cv_header_stdlib_h=$ac_header_preproc fi { $as_echo "$as_me:$LINENO: result: $ac_cv_header_stdlib_h" >&5 $as_echo "$ac_cv_header_stdlib_h" >&6; } fi if test "x$ac_cv_header_stdlib_h" = x""yes; then true else { { $as_echo "$as_me:$LINENO: error: stdlib.h is required" >&5 $as_echo "$as_me: error: stdlib.h is required" >&2;} { (exit 1); exit 1; }; } fi if test "${ac_cv_header_stdint_h+set}" = set; then { $as_echo "$as_me:$LINENO: checking for stdint.h" >&5 $as_echo_n "checking for stdint.h... " >&6; } if test "${ac_cv_header_stdint_h+set}" = set; then $as_echo_n "(cached) " >&6 fi { $as_echo "$as_me:$LINENO: result: $ac_cv_header_stdint_h" >&5 $as_echo "$ac_cv_header_stdint_h" >&6; } else # Is the header compilable? { $as_echo "$as_me:$LINENO: checking stdint.h usability" >&5 $as_echo_n "checking stdint.h usability... " >&6; } cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default #include _ACEOF rm -f conftest.$ac_objext if { (ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" $as_echo "$ac_try_echo") >&5 (eval "$ac_compile") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then ac_header_compiler=yes else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_compiler=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext { $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 $as_echo "$ac_header_compiler" >&6; } # Is the header present? { $as_echo "$as_me:$LINENO: checking stdint.h presence" >&5 $as_echo_n "checking stdint.h presence... " >&6; } cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include _ACEOF if { (ac_try="$ac_cpp conftest.$ac_ext" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" $as_echo "$ac_try_echo") >&5 (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null && { test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || test ! -s conftest.err }; then ac_header_preproc=yes else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_preproc=no fi rm -f conftest.err conftest.$ac_ext { $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 $as_echo "$ac_header_preproc" >&6; } # So? What about this header? case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in yes:no: ) { $as_echo "$as_me:$LINENO: WARNING: stdint.h: accepted by the compiler, rejected by the preprocessor!" >&5 $as_echo "$as_me: WARNING: stdint.h: accepted by the compiler, rejected by the preprocessor!" >&2;} { $as_echo "$as_me:$LINENO: WARNING: stdint.h: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: stdint.h: proceeding with the compiler's result" >&2;} ac_header_preproc=yes ;; no:yes:* ) { $as_echo "$as_me:$LINENO: WARNING: stdint.h: present but cannot be compiled" >&5 $as_echo "$as_me: WARNING: stdint.h: present but cannot be compiled" >&2;} { $as_echo "$as_me:$LINENO: WARNING: stdint.h: check for missing prerequisite headers?" >&5 $as_echo "$as_me: WARNING: stdint.h: check for missing prerequisite headers?" >&2;} { $as_echo "$as_me:$LINENO: WARNING: stdint.h: see the Autoconf documentation" >&5 $as_echo "$as_me: WARNING: stdint.h: see the Autoconf documentation" >&2;} { $as_echo "$as_me:$LINENO: WARNING: stdint.h: section \"Present But Cannot Be Compiled\"" >&5 $as_echo "$as_me: WARNING: stdint.h: section \"Present But Cannot Be Compiled\"" >&2;} { $as_echo "$as_me:$LINENO: WARNING: stdint.h: proceeding with the preprocessor's result" >&5 $as_echo "$as_me: WARNING: stdint.h: proceeding with the preprocessor's result" >&2;} { $as_echo "$as_me:$LINENO: WARNING: stdint.h: in the future, the compiler will take precedence" >&5 $as_echo "$as_me: WARNING: stdint.h: in the future, the compiler will take precedence" >&2;} ;; esac { $as_echo "$as_me:$LINENO: checking for stdint.h" >&5 $as_echo_n "checking for stdint.h... " >&6; } if test "${ac_cv_header_stdint_h+set}" = set; then $as_echo_n "(cached) " >&6 else ac_cv_header_stdint_h=$ac_header_preproc fi { $as_echo "$as_me:$LINENO: result: $ac_cv_header_stdint_h" >&5 $as_echo "$ac_cv_header_stdint_h" >&6; } fi if test "x$ac_cv_header_stdint_h" = x""yes; then true else { { $as_echo "$as_me:$LINENO: error: stdint.h is required" >&5 $as_echo "$as_me: error: stdint.h is required" >&2;} { (exit 1); exit 1; }; } fi if test "${ac_cv_header_unistd_h+set}" = set; then { $as_echo "$as_me:$LINENO: checking for unistd.h" >&5 $as_echo_n "checking for unistd.h... " >&6; } if test "${ac_cv_header_unistd_h+set}" = set; then $as_echo_n "(cached) " >&6 fi { $as_echo "$as_me:$LINENO: result: $ac_cv_header_unistd_h" >&5 $as_echo "$ac_cv_header_unistd_h" >&6; } else # Is the header compilable? { $as_echo "$as_me:$LINENO: checking unistd.h usability" >&5 $as_echo_n "checking unistd.h usability... " >&6; } cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default #include _ACEOF rm -f conftest.$ac_objext if { (ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" $as_echo "$ac_try_echo") >&5 (eval "$ac_compile") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then ac_header_compiler=yes else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_compiler=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext { $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 $as_echo "$ac_header_compiler" >&6; } # Is the header present? { $as_echo "$as_me:$LINENO: checking unistd.h presence" >&5 $as_echo_n "checking unistd.h presence... " >&6; } cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include _ACEOF if { (ac_try="$ac_cpp conftest.$ac_ext" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" $as_echo "$ac_try_echo") >&5 (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null && { test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || test ! -s conftest.err }; then ac_header_preproc=yes else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_preproc=no fi rm -f conftest.err conftest.$ac_ext { $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 $as_echo "$ac_header_preproc" >&6; } # So? What about this header? case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in yes:no: ) { $as_echo "$as_me:$LINENO: WARNING: unistd.h: accepted by the compiler, rejected by the preprocessor!" >&5 $as_echo "$as_me: WARNING: unistd.h: accepted by the compiler, rejected by the preprocessor!" >&2;} { $as_echo "$as_me:$LINENO: WARNING: unistd.h: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: unistd.h: proceeding with the compiler's result" >&2;} ac_header_preproc=yes ;; no:yes:* ) { $as_echo "$as_me:$LINENO: WARNING: unistd.h: present but cannot be compiled" >&5 $as_echo "$as_me: WARNING: unistd.h: present but cannot be compiled" >&2;} { $as_echo "$as_me:$LINENO: WARNING: unistd.h: check for missing prerequisite headers?" >&5 $as_echo "$as_me: WARNING: unistd.h: check for missing prerequisite headers?" >&2;} { $as_echo "$as_me:$LINENO: WARNING: unistd.h: see the Autoconf documentation" >&5 $as_echo "$as_me: WARNING: unistd.h: see the Autoconf documentation" >&2;} { $as_echo "$as_me:$LINENO: WARNING: unistd.h: section \"Present But Cannot Be Compiled\"" >&5 $as_echo "$as_me: WARNING: unistd.h: section \"Present But Cannot Be Compiled\"" >&2;} { $as_echo "$as_me:$LINENO: WARNING: unistd.h: proceeding with the preprocessor's result" >&5 $as_echo "$as_me: WARNING: unistd.h: proceeding with the preprocessor's result" >&2;} { $as_echo "$as_me:$LINENO: WARNING: unistd.h: in the future, the compiler will take precedence" >&5 $as_echo "$as_me: WARNING: unistd.h: in the future, the compiler will take precedence" >&2;} ;; esac { $as_echo "$as_me:$LINENO: checking for unistd.h" >&5 $as_echo_n "checking for unistd.h... " >&6; } if test "${ac_cv_header_unistd_h+set}" = set; then $as_echo_n "(cached) " >&6 else ac_cv_header_unistd_h=$ac_header_preproc fi { $as_echo "$as_me:$LINENO: result: $ac_cv_header_unistd_h" >&5 $as_echo "$ac_cv_header_unistd_h" >&6; } fi if test "x$ac_cv_header_unistd_h" = x""yes; then true else { { $as_echo "$as_me:$LINENO: error: unistd.h is required" >&5 $as_echo "$as_me: error: unistd.h is required" >&2;} { (exit 1); exit 1; }; } fi if test "${ac_cv_header_pthread_h+set}" = set; then { $as_echo "$as_me:$LINENO: checking for pthread.h" >&5 $as_echo_n "checking for pthread.h... " >&6; } if test "${ac_cv_header_pthread_h+set}" = set; then $as_echo_n "(cached) " >&6 fi { $as_echo "$as_me:$LINENO: result: $ac_cv_header_pthread_h" >&5 $as_echo "$ac_cv_header_pthread_h" >&6; } else # Is the header compilable? { $as_echo "$as_me:$LINENO: checking pthread.h usability" >&5 $as_echo_n "checking pthread.h usability... " >&6; } cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default #include _ACEOF rm -f conftest.$ac_objext if { (ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" $as_echo "$ac_try_echo") >&5 (eval "$ac_compile") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then ac_header_compiler=yes else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_compiler=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext { $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 $as_echo "$ac_header_compiler" >&6; } # Is the header present? { $as_echo "$as_me:$LINENO: checking pthread.h presence" >&5 $as_echo_n "checking pthread.h presence... " >&6; } cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include _ACEOF if { (ac_try="$ac_cpp conftest.$ac_ext" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" $as_echo "$ac_try_echo") >&5 (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null && { test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || test ! -s conftest.err }; then ac_header_preproc=yes else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_preproc=no fi rm -f conftest.err conftest.$ac_ext { $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 $as_echo "$ac_header_preproc" >&6; } # So? What about this header? case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in yes:no: ) { $as_echo "$as_me:$LINENO: WARNING: pthread.h: accepted by the compiler, rejected by the preprocessor!" >&5 $as_echo "$as_me: WARNING: pthread.h: accepted by the compiler, rejected by the preprocessor!" >&2;} { $as_echo "$as_me:$LINENO: WARNING: pthread.h: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: pthread.h: proceeding with the compiler's result" >&2;} ac_header_preproc=yes ;; no:yes:* ) { $as_echo "$as_me:$LINENO: WARNING: pthread.h: present but cannot be compiled" >&5 $as_echo "$as_me: WARNING: pthread.h: present but cannot be compiled" >&2;} { $as_echo "$as_me:$LINENO: WARNING: pthread.h: check for missing prerequisite headers?" >&5 $as_echo "$as_me: WARNING: pthread.h: check for missing prerequisite headers?" >&2;} { $as_echo "$as_me:$LINENO: WARNING: pthread.h: see the Autoconf documentation" >&5 $as_echo "$as_me: WARNING: pthread.h: see the Autoconf documentation" >&2;} { $as_echo "$as_me:$LINENO: WARNING: pthread.h: section \"Present But Cannot Be Compiled\"" >&5 $as_echo "$as_me: WARNING: pthread.h: section \"Present But Cannot Be Compiled\"" >&2;} { $as_echo "$as_me:$LINENO: WARNING: pthread.h: proceeding with the preprocessor's result" >&5 $as_echo "$as_me: WARNING: pthread.h: proceeding with the preprocessor's result" >&2;} { $as_echo "$as_me:$LINENO: WARNING: pthread.h: in the future, the compiler will take precedence" >&5 $as_echo "$as_me: WARNING: pthread.h: in the future, the compiler will take precedence" >&2;} ;; esac { $as_echo "$as_me:$LINENO: checking for pthread.h" >&5 $as_echo_n "checking for pthread.h... " >&6; } if test "${ac_cv_header_pthread_h+set}" = set; then $as_echo_n "(cached) " >&6 else ac_cv_header_pthread_h=$ac_header_preproc fi { $as_echo "$as_me:$LINENO: result: $ac_cv_header_pthread_h" >&5 $as_echo "$ac_cv_header_pthread_h" >&6; } fi if test "x$ac_cv_header_pthread_h" = x""yes; then true else { { $as_echo "$as_me:$LINENO: error: pthread.h is required" >&5 $as_echo "$as_me: error: pthread.h is required" >&2;} { (exit 1); exit 1; }; } fi if test "${ac_cv_header_tcutil_h+set}" = set; then { $as_echo "$as_me:$LINENO: checking for tcutil.h" >&5 $as_echo_n "checking for tcutil.h... " >&6; } if test "${ac_cv_header_tcutil_h+set}" = set; then $as_echo_n "(cached) " >&6 fi { $as_echo "$as_me:$LINENO: result: $ac_cv_header_tcutil_h" >&5 $as_echo "$ac_cv_header_tcutil_h" >&6; } else # Is the header compilable? { $as_echo "$as_me:$LINENO: checking tcutil.h usability" >&5 $as_echo_n "checking tcutil.h usability... " >&6; } cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default #include _ACEOF rm -f conftest.$ac_objext if { (ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" $as_echo "$ac_try_echo") >&5 (eval "$ac_compile") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then ac_header_compiler=yes else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_compiler=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext { $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 $as_echo "$ac_header_compiler" >&6; } # Is the header present? { $as_echo "$as_me:$LINENO: checking tcutil.h presence" >&5 $as_echo_n "checking tcutil.h presence... " >&6; } cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include _ACEOF if { (ac_try="$ac_cpp conftest.$ac_ext" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" $as_echo "$ac_try_echo") >&5 (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null && { test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || test ! -s conftest.err }; then ac_header_preproc=yes else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_preproc=no fi rm -f conftest.err conftest.$ac_ext { $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 $as_echo "$ac_header_preproc" >&6; } # So? What about this header? case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in yes:no: ) { $as_echo "$as_me:$LINENO: WARNING: tcutil.h: accepted by the compiler, rejected by the preprocessor!" >&5 $as_echo "$as_me: WARNING: tcutil.h: accepted by the compiler, rejected by the preprocessor!" >&2;} { $as_echo "$as_me:$LINENO: WARNING: tcutil.h: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: tcutil.h: proceeding with the compiler's result" >&2;} ac_header_preproc=yes ;; no:yes:* ) { $as_echo "$as_me:$LINENO: WARNING: tcutil.h: present but cannot be compiled" >&5 $as_echo "$as_me: WARNING: tcutil.h: present but cannot be compiled" >&2;} { $as_echo "$as_me:$LINENO: WARNING: tcutil.h: check for missing prerequisite headers?" >&5 $as_echo "$as_me: WARNING: tcutil.h: check for missing prerequisite headers?" >&2;} { $as_echo "$as_me:$LINENO: WARNING: tcutil.h: see the Autoconf documentation" >&5 $as_echo "$as_me: WARNING: tcutil.h: see the Autoconf documentation" >&2;} { $as_echo "$as_me:$LINENO: WARNING: tcutil.h: section \"Present But Cannot Be Compiled\"" >&5 $as_echo "$as_me: WARNING: tcutil.h: section \"Present But Cannot Be Compiled\"" >&2;} { $as_echo "$as_me:$LINENO: WARNING: tcutil.h: proceeding with the preprocessor's result" >&5 $as_echo "$as_me: WARNING: tcutil.h: proceeding with the preprocessor's result" >&2;} { $as_echo "$as_me:$LINENO: WARNING: tcutil.h: in the future, the compiler will take precedence" >&5 $as_echo "$as_me: WARNING: tcutil.h: in the future, the compiler will take precedence" >&2;} ;; esac { $as_echo "$as_me:$LINENO: checking for tcutil.h" >&5 $as_echo_n "checking for tcutil.h... " >&6; } if test "${ac_cv_header_tcutil_h+set}" = set; then $as_echo_n "(cached) " >&6 else ac_cv_header_tcutil_h=$ac_header_preproc fi { $as_echo "$as_me:$LINENO: result: $ac_cv_header_tcutil_h" >&5 $as_echo "$ac_cv_header_tcutil_h" >&6; } fi if test "x$ac_cv_header_tcutil_h" = x""yes; then true else { { $as_echo "$as_me:$LINENO: error: tcutil.h is required" >&5 $as_echo "$as_me: error: tcutil.h is required" >&2;} { (exit 1); exit 1; }; } fi if test "$enable_lua" = "yes" then if test "${ac_cv_header_lua_h+set}" = set; then { $as_echo "$as_me:$LINENO: checking for lua.h" >&5 $as_echo_n "checking for lua.h... " >&6; } if test "${ac_cv_header_lua_h+set}" = set; then $as_echo_n "(cached) " >&6 fi { $as_echo "$as_me:$LINENO: result: $ac_cv_header_lua_h" >&5 $as_echo "$ac_cv_header_lua_h" >&6; } else # Is the header compilable? { $as_echo "$as_me:$LINENO: checking lua.h usability" >&5 $as_echo_n "checking lua.h usability... " >&6; } cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default #include _ACEOF rm -f conftest.$ac_objext if { (ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" $as_echo "$ac_try_echo") >&5 (eval "$ac_compile") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then ac_header_compiler=yes else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_compiler=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext { $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 $as_echo "$ac_header_compiler" >&6; } # Is the header present? { $as_echo "$as_me:$LINENO: checking lua.h presence" >&5 $as_echo_n "checking lua.h presence... " >&6; } cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include _ACEOF if { (ac_try="$ac_cpp conftest.$ac_ext" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" $as_echo "$ac_try_echo") >&5 (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null && { test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || test ! -s conftest.err }; then ac_header_preproc=yes else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_preproc=no fi rm -f conftest.err conftest.$ac_ext { $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 $as_echo "$ac_header_preproc" >&6; } # So? What about this header? case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in yes:no: ) { $as_echo "$as_me:$LINENO: WARNING: lua.h: accepted by the compiler, rejected by the preprocessor!" >&5 $as_echo "$as_me: WARNING: lua.h: accepted by the compiler, rejected by the preprocessor!" >&2;} { $as_echo "$as_me:$LINENO: WARNING: lua.h: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: lua.h: proceeding with the compiler's result" >&2;} ac_header_preproc=yes ;; no:yes:* ) { $as_echo "$as_me:$LINENO: WARNING: lua.h: present but cannot be compiled" >&5 $as_echo "$as_me: WARNING: lua.h: present but cannot be compiled" >&2;} { $as_echo "$as_me:$LINENO: WARNING: lua.h: check for missing prerequisite headers?" >&5 $as_echo "$as_me: WARNING: lua.h: check for missing prerequisite headers?" >&2;} { $as_echo "$as_me:$LINENO: WARNING: lua.h: see the Autoconf documentation" >&5 $as_echo "$as_me: WARNING: lua.h: see the Autoconf documentation" >&2;} { $as_echo "$as_me:$LINENO: WARNING: lua.h: section \"Present But Cannot Be Compiled\"" >&5 $as_echo "$as_me: WARNING: lua.h: section \"Present But Cannot Be Compiled\"" >&2;} { $as_echo "$as_me:$LINENO: WARNING: lua.h: proceeding with the preprocessor's result" >&5 $as_echo "$as_me: WARNING: lua.h: proceeding with the preprocessor's result" >&2;} { $as_echo "$as_me:$LINENO: WARNING: lua.h: in the future, the compiler will take precedence" >&5 $as_echo "$as_me: WARNING: lua.h: in the future, the compiler will take precedence" >&2;} ;; esac { $as_echo "$as_me:$LINENO: checking for lua.h" >&5 $as_echo_n "checking for lua.h... " >&6; } if test "${ac_cv_header_lua_h+set}" = set; then $as_echo_n "(cached) " >&6 else ac_cv_header_lua_h=$ac_header_preproc fi { $as_echo "$as_me:$LINENO: result: $ac_cv_header_lua_h" >&5 $as_echo "$ac_cv_header_lua_h" >&6; } fi if test "x$ac_cv_header_lua_h" = x""yes; then true else { { $as_echo "$as_me:$LINENO: error: lua.h is required" >&5 $as_echo "$as_me: error: lua.h is required" >&2;} { (exit 1); exit 1; }; } fi fi # Shared libraries if test "$enable_shared" != "no" && test "$enable_profile" != "yes" then if uname | grep Darwin >/dev/null then MYLIBRARYFILES="$MYLIBRARYFILES libtokyotyrant.$MYLIBVER.$MYLIBREV.0.dylib" MYLIBRARYFILES="$MYLIBRARYFILES libtokyotyrant.$MYLIBVER.dylib" MYLIBRARYFILES="$MYLIBRARYFILES libtokyotyrant.dylib" MYLIBRARYFILES="$MYLIBRARYFILES ttskelmock.bundle ttskeldir.bundle ttskelproxy.bundle" MYLIBRARYFILES="$MYLIBRARYFILES ttskelnull.bundle" MYLDLIBPATHENV="DYLD_LIBRARY_PATH" else MYLIBRARYFILES="$MYLIBRARYFILES libtokyotyrant.so.$MYLIBVER.$MYLIBREV.0" MYLIBRARYFILES="$MYLIBRARYFILES libtokyotyrant.so.$MYLIBVER" MYLIBRARYFILES="$MYLIBRARYFILES libtokyotyrant.so" MYLIBRARYFILES="$MYLIBRARYFILES ttskelmock.so ttskeldir.so ttskelproxy.so" MYLIBRARYFILES="$MYLIBRARYFILES ttskelnull.so" fi fi #================================================================ # Generic Settings #================================================================ # Export variables # Targets ac_config_files="$ac_config_files Makefile tokyotyrant.pc" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure # scripts and configure runs, see configure's option --config-cache. # It is not useful on other systems. If it contains results you don't # want to keep, you may remove or edit it. # # config.status only pays attention to the cache file if you give it # the --recheck option to rerun configure. # # `ac_cv_env_foo' variables (set or unset) will be overridden when # loading this file, other *unset* `ac_cv_foo' will be assigned the # following values. _ACEOF # The following way of writing the cache mishandles newlines in values, # but we know of no workaround that is simple, portable, and efficient. # So, we kill variables containing newlines. # Ultrix sh set writes to stderr and can't be redirected directly, # and sets the high bit in the cache file unless we assign to the vars. ( for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { $as_echo "$as_me:$LINENO: WARNING: cache variable $ac_var contains a newline" >&5 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) $as_unset $ac_var ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space=' '; set) 2>&1` in #( *${as_nl}ac_space=\ *) # `set' does not quote correctly, so add quotes (double-quote # substitution turns \\\\ into \\, and sed turns \\ into \). sed -n \ "s/'/'\\\\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" ;; #( *) # `set' quotes correctly as required by POSIX, so do not add quotes. sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) | sed ' /^ac_cv_env_/b end t clear :clear s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ t end s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ :end' >>confcache if diff "$cache_file" confcache >/dev/null 2>&1; then :; else if test -w "$cache_file"; then test "x$cache_file" != "x/dev/null" && { $as_echo "$as_me:$LINENO: updating cache $cache_file" >&5 $as_echo "$as_me: updating cache $cache_file" >&6;} cat confcache >$cache_file else { $as_echo "$as_me:$LINENO: not updating unwritable cache $cache_file" >&5 $as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} fi fi rm -f confcache test "x$prefix" = xNONE && prefix=$ac_default_prefix # Let make expand exec_prefix. test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' # Transform confdefs.h into DEFS. # Protect against shell expansion while executing Makefile rules. # Protect against Makefile macro expansion. # # If the first sed substitution is executed (which looks for macros that # take arguments), then branch to the quote section. Otherwise, # look for a macro that doesn't take arguments. ac_script=' :mline /\\$/{ N s,\\\n,, b mline } t clear :clear s/^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*([^)]*)\)[ ]*\(.*\)/-D\1=\2/g t quote s/^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\)/-D\1=\2/g t quote b any :quote s/[ `~#$^&*(){}\\|;'\''"<>?]/\\&/g s/\[/\\&/g s/\]/\\&/g s/\$/$$/g H :any ${ g s/^\n// s/\n/ /g p } ' DEFS=`sed -n "$ac_script" confdefs.h` ac_libobjs= ac_ltlibobjs= for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue # 1. Remove the extension, and $U if already installed. ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' ac_i=`$as_echo "$ac_i" | sed "$ac_script"` # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR # will be set to the directory where LIBOBJS objects are built. ac_libobjs="$ac_libobjs \${LIBOBJDIR}$ac_i\$U.$ac_objext" ac_ltlibobjs="$ac_ltlibobjs \${LIBOBJDIR}$ac_i"'$U.lo' done LIBOBJS=$ac_libobjs LTLIBOBJS=$ac_ltlibobjs : ${CONFIG_STATUS=./config.status} ac_write_fail=0 ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files $CONFIG_STATUS" { $as_echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5 $as_echo "$as_me: creating $CONFIG_STATUS" >&6;} cat >$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 #! $SHELL # Generated by $as_me. # Run this file to recreate the current configuration. # Compiler output produced by configure, useful for debugging # configure, is in config.log if it exists. debug=false ac_cs_recheck=false ac_cs_silent=false SHELL=\${CONFIG_SHELL-$SHELL} _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 ## --------------------- ## ## M4sh Initialization. ## ## --------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in *posix*) set -o posix ;; esac fi # PATH needs CR # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo if (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # Support unset when possible. if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then as_unset=unset else as_unset=false fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. case $0 in *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 { (exit 1); exit 1; } fi # Work around bugs in pre-3.0 UWIN ksh. for as_var in ENV MAIL MAILPATH do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # Required to use basename. if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi # Name of the executable. as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # CDPATH. $as_unset CDPATH as_lineno_1=$LINENO as_lineno_2=$LINENO test "x$as_lineno_1" != "x$as_lineno_2" && test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || { # Create $as_me.lineno as a copy of $as_myself, but with $LINENO # uniformly replaced by the line number. The first 'sed' inserts a # line-number line after each line using $LINENO; the second 'sed' # does the real work. The second script uses 'N' to pair each # line-number line with the line containing $LINENO, and appends # trailing '-' during substitution so that $LINENO is not a special # case at line end. # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the # scripts with optimization help from Paolo Bonzini. Blame Lee # E. McMahon (1931-1989) for sed's syntax. :-) sed -n ' p /[$]LINENO/= ' <$as_myself | sed ' s/[$]LINENO.*/&-/ t lineno b :lineno N :loop s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ t loop s/-\n.*// ' >$as_me.lineno && chmod +x "$as_me.lineno" || { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 { (exit 1); exit 1; }; } # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensitive to this). . "./$as_me.lineno" # Exit status is that of the last command. exit } if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in -n*) case `echo 'x\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. *) ECHO_C='\c';; esac;; *) ECHO_N='-n';; esac if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -p'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -p' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -p' fi else as_ln_s='cp -p' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null if mkdir -p . 2>/dev/null; then as_mkdir_p=: else test -d ./-p && rmdir ./-p as_mkdir_p=false fi if test -x / >/dev/null 2>&1; then as_test_x='test -x' else if ls -dL / >/dev/null 2>&1; then as_ls_L_option=L else as_ls_L_option= fi as_test_x=' eval sh -c '\'' if test -d "$1"; then test -d "$1/."; else case $1 in -*)set "./$1";; esac; case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in ???[sx]*):;;*)false;;esac;fi '\'' sh ' fi as_executable_p=$as_test_x # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" exec 6>&1 # Save the log message, to keep $[0] and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" This file was extended by tokyotyrant $as_me 1.1.40, which was generated by GNU Autoconf 2.63. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS CONFIG_COMMANDS = $CONFIG_COMMANDS $ $0 $@ on `(hostname || uname -n) 2>/dev/null | sed 1q` " _ACEOF case $ac_config_files in *" "*) set x $ac_config_files; shift; ac_config_files=$*;; esac cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # Files that config.status was made for. config_files="$ac_config_files" _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 ac_cs_usage="\ \`$as_me' instantiates files from templates according to the current configuration. Usage: $0 [OPTION]... [FILE]... -h, --help print this help, then exit -V, --version print version number and configuration settings, then exit -q, --quiet, --silent do not print progress messages -d, --debug don't remove temporary files --recheck update $as_me by reconfiguring in the same conditions --file=FILE[:TEMPLATE] instantiate the configuration file FILE Configuration files: $config_files Report bugs to ." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_version="\\ tokyotyrant config.status 1.1.40 configured by $0, generated by GNU Autoconf 2.63, with options \\"`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\" Copyright (C) 2008 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." ac_pwd='$ac_pwd' srcdir='$srcdir' test -n "\$AWK" || AWK=awk _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # The default lists apply if the user does not specify any file. ac_need_defaults=: while test $# != 0 do case $1 in --*=*) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` ac_shift=: ;; *) ac_option=$1 ac_optarg=$2 ac_shift=shift ;; esac case $ac_option in # Handling of the options. -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) ac_cs_recheck=: ;; --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) $as_echo "$ac_cs_version"; exit ;; --debug | --debu | --deb | --de | --d | -d ) debug=: ;; --file | --fil | --fi | --f ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; esac CONFIG_FILES="$CONFIG_FILES '$ac_optarg'" ac_need_defaults=false;; --he | --h | --help | --hel | -h ) $as_echo "$ac_cs_usage"; exit ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil | --si | --s) ac_cs_silent=: ;; # This is an error. -*) { $as_echo "$as_me: error: unrecognized option: $1 Try \`$0 --help' for more information." >&2 { (exit 1); exit 1; }; } ;; *) ac_config_targets="$ac_config_targets $1" ac_need_defaults=false ;; esac shift done ac_configure_extra_args= if $ac_cs_silent; then exec 6>/dev/null ac_configure_extra_args="$ac_configure_extra_args --silent" fi _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 if \$ac_cs_recheck; then set X '$SHELL' '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion shift \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 CONFIG_SHELL='$SHELL' export CONFIG_SHELL exec "\$@" fi _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 exec 5>>config.log { echo sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX ## Running $as_me. ## _ASBOX $as_echo "$ac_log" } >&5 _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Handling of arguments. for ac_config_target in $ac_config_targets do case $ac_config_target in "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; "tokyotyrant.pc") CONFIG_FILES="$CONFIG_FILES tokyotyrant.pc" ;; *) { { $as_echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5 $as_echo "$as_me: error: invalid argument: $ac_config_target" >&2;} { (exit 1); exit 1; }; };; esac done # If the user did not use the arguments to specify the items to instantiate, # then the envvar interface is used. Set only those that are not. # We use the long form for the default assignment because of an extremely # bizarre bug on SunOS 4.1.3. if $ac_need_defaults; then test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files fi # Have a temporary directory for convenience. Make it in the build tree # simply because there is no reason against having it here, and in addition, # creating and moving files from /tmp can sometimes cause problems. # Hook for its removal unless debugging. # Note that there is a small window in which the directory will not be cleaned: # after its creation but before its name has been assigned to `$tmp'. $debug || { tmp= trap 'exit_status=$? { test -z "$tmp" || test ! -d "$tmp" || rm -fr "$tmp"; } && exit $exit_status ' 0 trap '{ (exit 1); exit 1; }' 1 2 13 15 } # Create a (secure) tmp directory for tmp files. { tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" } || { tmp=./conf$$-$RANDOM (umask 077 && mkdir "$tmp") } || { $as_echo "$as_me: cannot create a temporary directory in ." >&2 { (exit 1); exit 1; } } # Set up the scripts for CONFIG_FILES section. # No need to generate them if there are no CONFIG_FILES. # This happens for instance with `./config.status config.h'. if test -n "$CONFIG_FILES"; then ac_cr=' ' ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then ac_cs_awk_cr='\\r' else ac_cs_awk_cr=$ac_cr fi echo 'BEGIN {' >"$tmp/subs1.awk" && _ACEOF { echo "cat >conf$$subs.awk <<_ACEOF" && echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && echo "_ACEOF" } >conf$$subs.sh || { { $as_echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5 $as_echo "$as_me: error: could not make $CONFIG_STATUS" >&2;} { (exit 1); exit 1; }; } ac_delim_num=`echo "$ac_subst_vars" | grep -c '$'` ac_delim='%!_!# ' for ac_last_try in false false false false false :; do . ./conf$$subs.sh || { { $as_echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5 $as_echo "$as_me: error: could not make $CONFIG_STATUS" >&2;} { (exit 1); exit 1; }; } ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` if test $ac_delim_n = $ac_delim_num; then break elif $ac_last_try; then { { $as_echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5 $as_echo "$as_me: error: could not make $CONFIG_STATUS" >&2;} { (exit 1); exit 1; }; } else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done rm -f conf$$subs.sh cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 cat >>"\$tmp/subs1.awk" <<\\_ACAWK && _ACEOF sed -n ' h s/^/S["/; s/!.*/"]=/ p g s/^[^!]*!// :repl t repl s/'"$ac_delim"'$// t delim :nl h s/\(.\{148\}\).*/\1/ t more1 s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ p n b repl :more1 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t nl :delim h s/\(.\{148\}\).*/\1/ t more2 s/["\\]/\\&/g; s/^/"/; s/$/"/ p b :more2 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t delim ' >$CONFIG_STATUS || ac_write_fail=1 rm -f conf$$subs.awk cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACAWK cat >>"\$tmp/subs1.awk" <<_ACAWK && for (key in S) S_is_set[key] = 1 FS = "" } { line = $ 0 nfields = split(line, field, "@") substed = 0 len = length(field[1]) for (i = 2; i < nfields; i++) { key = field[i] keylen = length(key) if (S_is_set[key]) { value = S[key] line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) len += length(value) + length(field[++i]) substed = 1 } else len += 1 + keylen } print line } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" else cat fi < "$tmp/subs1.awk" > "$tmp/subs.awk" \ || { { $as_echo "$as_me:$LINENO: error: could not setup config files machinery" >&5 $as_echo "$as_me: error: could not setup config files machinery" >&2;} { (exit 1); exit 1; }; } _ACEOF # VPATH may cause trouble with some makes, so we remove $(srcdir), # ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and # trailing colons and then remove the whole line if VPATH becomes empty # (actually we leave an empty line to preserve line numbers). if test "x$srcdir" = x.; then ac_vpsub='/^[ ]*VPATH[ ]*=/{ s/:*\$(srcdir):*/:/ s/:*\${srcdir}:*/:/ s/:*@srcdir@:*/:/ s/^\([^=]*=[ ]*\):*/\1/ s/:*$// s/^[^=]*=[ ]*$// }' fi cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 fi # test -n "$CONFIG_FILES" eval set X " :F $CONFIG_FILES " shift for ac_tag do case $ac_tag in :[FHLC]) ac_mode=$ac_tag; continue;; esac case $ac_mode$ac_tag in :[FHL]*:*);; :L* | :C*:*) { { $as_echo "$as_me:$LINENO: error: invalid tag $ac_tag" >&5 $as_echo "$as_me: error: invalid tag $ac_tag" >&2;} { (exit 1); exit 1; }; };; :[FH]-) ac_tag=-:-;; :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; esac ac_save_IFS=$IFS IFS=: set x $ac_tag IFS=$ac_save_IFS shift ac_file=$1 shift case $ac_mode in :L) ac_source=$1;; :[FH]) ac_file_inputs= for ac_f do case $ac_f in -) ac_f="$tmp/stdin";; *) # Look for the file first in the build tree, then in the source tree # (if the path is not absolute). The absolute path cannot be DOS-style, # because $ac_f cannot contain `:'. test -f "$ac_f" || case $ac_f in [\\/$]*) false;; *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; esac || { { $as_echo "$as_me:$LINENO: error: cannot find input file: $ac_f" >&5 $as_echo "$as_me: error: cannot find input file: $ac_f" >&2;} { (exit 1); exit 1; }; };; esac case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac ac_file_inputs="$ac_file_inputs '$ac_f'" done # Let's still pretend it is `configure' which instantiates (i.e., don't # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ configure_input='Generated from '` $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' `' by configure.' if test x"$ac_file" != x-; then configure_input="$ac_file. $configure_input" { $as_echo "$as_me:$LINENO: creating $ac_file" >&5 $as_echo "$as_me: creating $ac_file" >&6;} fi # Neutralize special characters interpreted by sed in replacement strings. case $configure_input in #( *\&* | *\|* | *\\* ) ac_sed_conf_input=`$as_echo "$configure_input" | sed 's/[\\\\&|]/\\\\&/g'`;; #( *) ac_sed_conf_input=$configure_input;; esac case $ac_tag in *:-:* | *:-) cat >"$tmp/stdin" \ || { { $as_echo "$as_me:$LINENO: error: could not create $ac_file" >&5 $as_echo "$as_me: error: could not create $ac_file" >&2;} { (exit 1); exit 1; }; } ;; esac ;; esac ac_dir=`$as_dirname -- "$ac_file" || $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$ac_file" : 'X\(//\)[^/]' \| \ X"$ac_file" : 'X\(//\)$' \| \ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$ac_file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` { as_dir="$ac_dir" case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || { $as_mkdir_p && mkdir -p "$as_dir"; } || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || { { $as_echo "$as_me:$LINENO: error: cannot create directory $as_dir" >&5 $as_echo "$as_me: error: cannot create directory $as_dir" >&2;} { (exit 1); exit 1; }; }; } ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix case $ac_mode in :F) # # CONFIG_FILE # _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # If the template does not know about datarootdir, expand it. # FIXME: This hack should be removed a few years after 2.60. ac_datarootdir_hack=; ac_datarootdir_seen= ac_sed_dataroot=' /datarootdir/ { p q } /@datadir@/p /@docdir@/p /@infodir@/p /@localedir@/p /@mandir@/p ' case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in *datarootdir*) ac_datarootdir_seen=yes;; *@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) { $as_echo "$as_me:$LINENO: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 $as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_datarootdir_hack=' s&@datadir@&$datadir&g s&@docdir@&$docdir&g s&@infodir@&$infodir&g s&@localedir@&$localedir&g s&@mandir@&$mandir&g s&\\\${datarootdir}&$datarootdir&g' ;; esac _ACEOF # Neutralize VPATH when `$srcdir' = `.'. # Shell code in configure.ac might set extrasub. # FIXME: do we really want to maintain this feature? cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_sed_extra="$ac_vpsub $extrasub _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 :t /@[a-zA-Z_][a-zA-Z_0-9]*@/!b s|@configure_input@|$ac_sed_conf_input|;t t s&@top_builddir@&$ac_top_builddir_sub&;t t s&@top_build_prefix@&$ac_top_build_prefix&;t t s&@srcdir@&$ac_srcdir&;t t s&@abs_srcdir@&$ac_abs_srcdir&;t t s&@top_srcdir@&$ac_top_srcdir&;t t s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t s&@builddir@&$ac_builddir&;t t s&@abs_builddir@&$ac_abs_builddir&;t t s&@abs_top_builddir@&$ac_abs_top_builddir&;t t $ac_datarootdir_hack " eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$tmp/subs.awk" >$tmp/out \ || { { $as_echo "$as_me:$LINENO: error: could not create $ac_file" >&5 $as_echo "$as_me: error: could not create $ac_file" >&2;} { (exit 1); exit 1; }; } test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && { ac_out=`sed -n '/\${datarootdir}/p' "$tmp/out"`; test -n "$ac_out"; } && { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' "$tmp/out"`; test -z "$ac_out"; } && { $as_echo "$as_me:$LINENO: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined." >&5 $as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined." >&2;} rm -f "$tmp/stdin" case $ac_file in -) cat "$tmp/out" && rm -f "$tmp/out";; *) rm -f "$ac_file" && mv "$tmp/out" "$ac_file";; esac \ || { { $as_echo "$as_me:$LINENO: error: could not create $ac_file" >&5 $as_echo "$as_me: error: could not create $ac_file" >&2;} { (exit 1); exit 1; }; } ;; esac done # for ac_tag { (exit 0); exit 0; } _ACEOF chmod +x $CONFIG_STATUS ac_clean_files=$ac_clean_files_save test $ac_write_fail = 0 || { { $as_echo "$as_me:$LINENO: error: write failure creating $CONFIG_STATUS" >&5 $as_echo "$as_me: error: write failure creating $CONFIG_STATUS" >&2;} { (exit 1); exit 1; }; } # configure is writing to config.log, and then calls config.status. # config.status does its own redirection, appending to config.log. # Unfortunately, on DOS this fails, as config.log is still kept open # by configure, so config.status won't be able to write to it; its # output is simply discarded. So we exec the FD to /dev/null, # effectively closing config.log, so it can be properly (re)opened and # appended to by config.status. When coming back to configure, we # need to make the FD available again. if test "$no_create" != yes; then ac_cs_success=: ac_config_status_args= test "$silent" = yes && ac_config_status_args="$ac_config_status_args --quiet" exec 5>/dev/null $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false exec 5>>config.log # Use ||, not &&, to avoid exiting from the if with $? = 1, which # would make configure fail if this is the last instruction. $ac_cs_success || { (exit 1); exit 1; } fi if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then { $as_echo "$as_me:$LINENO: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} fi # Messages printf '#================================================================\n' printf '# Ready to make.\n' printf '#================================================================\n' # END OF FILE tokyotyrant-1.1.40/configure.in000066400000000000000000000226071133351147200165240ustar00rootroot00000000000000# Source of configuration for Tokyo Tyrant #================================================================ # Generic Settings #================================================================ # Package name AC_INIT(tokyotyrant, 1.1.40) # Package information MYLIBVER=3 MYLIBREV=23 MYPROTVER="0.91" # Targets MYHEADERFILES="ttutil.h tculog.h tcrdb.h" MYLIBRARYFILES="libtokyotyrant.a" MYLIBOBJFILES="ttutil.o tculog.o tcrdb.o myconf.o" MYCOMMANDFILES="ttserver ttulmgr ttultest tcrtest tcrmttest tcrmgr" MYINITFILES="ttservctl" MYMAN1FILES="ttserver.1 ttulmgr.1 ttultest.1 tcrtest.1 tcrmttest.1 tcrmgr.1" MYMAN3FILES="ttutil.3 tculog.3 tcrdb.3" MYMAN8FILES="ttservctl.8" MYSCREXTFILES="ext" MYDOCUMENTFILES="COPYING ChangeLog THANKS doc" MYPCFILES="tokyotyrant.pc" # Building flags MYCFLAGS="-std=c99 -Wall -fPIC -fsigned-char -O2" MYCPPFLAGS="-I. -I\$(INCLUDEDIR) -I$HOME/include -I/usr/local/include" MYCPPFLAGS="$MYCPPFLAGS -DNDEBUG -D_GNU_SOURCE=1 -D_REENTRANT -D__EXTENSIONS__" MYLDFLAGS="-L. -L\$(LIBDIR) -L$HOME/lib -L/usr/local/lib" MYCMDLDFLAGS="" MYRUNPATH="\$(LIBDIR)" MYLDLIBPATHENV="LD_LIBRARY_PATH" MYPOSTCMD="true" # Building paths PATH="$PATH:$HOME/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin" PATH="$PATH:/opt/SUNWspro/bin:/usr/ccs/bin:/usr/xpg4/bin:/usr/xpg6/bin:/usr/ucb" CPATH="$HOME/include:/usr/local/include:$CPATH" LIBRARY_PATH="$HOME/lib:/usr/local/lib:$LIBRARY_PATH" LD_LIBRARY_PATH="$HOME/lib:/usr/local/lib:$LD_LIBRARY_PATH" PKG_CONFIG_PATH="$HOME/lib/pkgconfig:/usr/local/lib/pkgconfig:$PKG_CONFIG_PATH" export PATH CPATH LIBRARY_PATH LD_LIBRARY_PATH PKG_CONFIG_PATH if type pkg-config > /dev/null 2>&1 then PATH="$PATH:`pkg-config --variable=bindir tokyocabinet`" CPATH="$CPATH:`pkg-config --variable=includedir tokyocabinet`" LIBRARY_PATH="$LIBRARY_PATH:`pkg-config --variable=libdir tokyocabinet`" LD_LIBRARY_PATH="$LD_LIBRARY_PATH:`pkg-config --variable=libdir tokyocabinet`" fi #================================================================ # Options #================================================================ # Internal variables enables="" # Debug mode AC_ARG_ENABLE(debug, AC_HELP_STRING([--enable-debug], [build for debugging])) if test "$enable_debug" = "yes" then MYCFLAGS="-std=c99 -Wall -fPIC -pedantic -fsigned-char -g -O0" MYCPPFLAGS="$MYCPPFLAGS -UNDEBUG" MYCMDLDFLAGS="$MYCMDLDFLAGS -static" enables="$enables (debug)" fi # Developping mode AC_ARG_ENABLE(devel, AC_HELP_STRING([--enable-devel], [build for development])) if test "$enable_devel" = "yes" then MYCFLAGS="-std=c99 -Wall -fPIC -pedantic -fsigned-char -g -O2 -pipe" MYCPPFLAGS="$MYCPPFLAGS -UNDEBUG" enables="$enables (devel)" fi # Profiling mode AC_ARG_ENABLE(profile, AC_HELP_STRING([--enable-profile], [build for profiling])) if test "$enable_profile" = "yes" then MYCFLAGS="-std=c99 -Wall -fPIC -pedantic -fsigned-char -g -pg -O2 -pipe" enables="$enables (profile)" fi # Static mode AC_ARG_ENABLE(static, AC_HELP_STRING([--enable-static], [build by static linking])) if test "$enable_static" = "yes" then MYCMDLDFLAGS="$MYCMDLDFLAGS -static" enables="$enables (static)" fi # Disable shared object AC_ARG_ENABLE(shared, AC_HELP_STRING([--disable-shared], [avoid to build shared libraries])) if test "$enable_shared" = "no" then enables="$enables (no-shared)" fi # Enable Lua extension AC_ARG_ENABLE(lua, AC_HELP_STRING([--enable-lua], [build with Lua extension])) if test "$enable_lua" = "yes" then enables="$enables (lua)" luaver=`lua -e 'v = string.gsub(_VERSION, ".* ", ""); print(v)'` MYCPPFLAGS="$MYCPPFLAGS -I/usr/include/lua$luaver -I/usr/local/include/lua$luaver" MYCPPFLAGS="$MYCPPFLAGS -I/usr/include/lua -I/usr/local/include/lua -D_MYLUA" MYLDFLAGS="$MYLDFLAGS -L/usr/include/lua$luaver -L/usr/local/include/lua$luaver" MYLDFLAGS="$MYLDFLAGS -L/usr/include/lua -L/usr/local/include/lua" CPATH="$CPATH:/usr/include/lua$luaver:/usr/local/include/lua$luaver" CPATH="$CPATH:/usr/include/lua:/usr/local/include/lua" LIBRARY_PATH="$LIBRARY_PATH:/usr/lib/lua$luaver:/usr/local/lib/lua$luaver" LIBRARY_PATH="$LIBRARY_PATH:/usr/lib/lua:/usr/local/lib/lua" LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/include/lua$luaver:/usr/local/include/lua$luaver" LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/include/lua:/usr/local/include/lua" fi # Specify the installation path of Tokyo Cabinet AC_ARG_WITH(tc, AC_HELP_STRING([--with-tc=DIR], [search DIR/include and DIR/lib for Tokyo Cabinet])) if test -n "$with_tc" then MYCPPFLAGS="$MYCPPFLAGS -I$with_tc/include" MYLDFLAGS="$MYLDFLAGS -L$with_tc/lib" MYRUNPATH="$MYRUNPATH:$with_tc/lib" CPATH="$CPATH:$with_tc/include" LIBRARY_PATH="$LIBRARY_PATH:$with_tc/lib" LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$with_tc/lib" fi # Specify the installation path of ZLIB AC_ARG_WITH(zlib, AC_HELP_STRING([--with-zlib=DIR], [search DIR/include and DIR/lib for ZLIB])) if test -n "$with_zlib" then MYCPPFLAGS="$MYCPPFLAGS -I$with_zlib/include" MYLDFLAGS="$MYLDFLAGS -L$with_zlib/lib" MYRUNPATH="$MYRUNPATH:$with_zlib/lib" CPATH="$CPATH:$with_zlib/include" LIBRARY_PATH="$LIBRARY_PATH:$with_zlib/lib" LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$with_zlib/lib" fi # Specify the installation path of BZIP2 AC_ARG_WITH(bzip, AC_HELP_STRING([--with-bzip=DIR], [search DIR/include and DIR/lib for BZIP2])) if test -n "$with_bzip" then MYCPPFLAGS="$MYCPPFLAGS -I$with_bzip/include" MYLDFLAGS="$MYLDFLAGS -L$with_bzip/lib" MYRUNPATH="$MYRUNPATH:$with_bzip/lib" CPATH="$CPATH:$with_bzip/include" LIBRARY_PATH="$LIBRARY_PATH:$with_bzip/lib" LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$with_bzip/lib" fi # Specify the installation path of Lua AC_ARG_WITH(lua, AC_HELP_STRING([--with-lua=DIR], [search DIR/include and DIR/lib for Lua])) if test -n "$with_lua" then MYCPPFLAGS="$MYCPPFLAGS -I$with_lua/include" MYLDFLAGS="$MYLDFLAGS -L$with_lua/lib" MYRUNPATH="$MYRUNPATH:$with_lua/lib" CPATH="$CPATH:$with_lua/include" LIBRARY_PATH="$LIBRARY_PATH:$with_lua/lib" LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$with_lua/lib" fi # Messages printf '#================================================================\n' printf '# Configuring Tokyo Tyrant version %s%s.\n' "$PACKAGE_VERSION" "$enables" printf '#================================================================\n' #================================================================ # Checking Commands and Libraries #================================================================ # C compiler AC_PROG_CC # Reset variables if test "$GCC" != "yes" then AC_MSG_WARN([another compiler except for GCC was detected]) MYCFLAGS="" fi test -n "$CFLAGS" && MYCFLAGS="$CFLAGS $MYCFLAGS" test -n "$CPPFLAGS" && MYCPPFLAGS="$CPPFLAGS $MYCPPFLAGS" test -n "$LDFLAGS" && MYLDFLAGS="$LDFLAGS $MYLDFLAGS" # Byte order AC_C_BIGENDIAN(MYCPPFLAGS="$MYCPPFLAGS -D_MYBIGEND") # Underlying libraries AC_CHECK_LIB(c, main) AC_CHECK_LIB(m, main) AC_CHECK_LIB(pthread, main) AC_CHECK_LIB(rt, main) AC_CHECK_LIB(dl, main) AC_CHECK_LIB(nsl, main) AC_CHECK_LIB(socket, main) AC_CHECK_LIB(resolv, main) AC_CHECK_LIB(z, main) AC_CHECK_LIB(bz2, main) AC_CHECK_LIB(tokyocabinet, main) if test "$enable_lua" = "yes" then AC_CHECK_LIB(lua, main) AC_CHECK_LIB(lua$luaver, main) fi AC_CHECK_LIB(tokyotyrant, main, AC_MSG_WARN([old version of Tokyo Tyrant was detected])) # Necessary headers AC_CHECK_HEADER(stdlib.h, true, AC_MSG_ERROR([stdlib.h is required])) AC_CHECK_HEADER(stdint.h, true, AC_MSG_ERROR([stdint.h is required])) AC_CHECK_HEADER(unistd.h, true, AC_MSG_ERROR([unistd.h is required])) AC_CHECK_HEADER(pthread.h, true, AC_MSG_ERROR([pthread.h is required])) AC_CHECK_HEADER(tcutil.h, true, AC_MSG_ERROR([tcutil.h is required])) if test "$enable_lua" = "yes" then AC_CHECK_HEADER(lua.h, true, AC_MSG_ERROR([lua.h is required])) fi # Shared libraries if test "$enable_shared" != "no" && test "$enable_profile" != "yes" then if uname | grep Darwin >/dev/null then MYLIBRARYFILES="$MYLIBRARYFILES libtokyotyrant.$MYLIBVER.$MYLIBREV.0.dylib" MYLIBRARYFILES="$MYLIBRARYFILES libtokyotyrant.$MYLIBVER.dylib" MYLIBRARYFILES="$MYLIBRARYFILES libtokyotyrant.dylib" MYLIBRARYFILES="$MYLIBRARYFILES ttskelmock.bundle ttskeldir.bundle ttskelproxy.bundle" MYLIBRARYFILES="$MYLIBRARYFILES ttskelnull.bundle" MYLDLIBPATHENV="DYLD_LIBRARY_PATH" else MYLIBRARYFILES="$MYLIBRARYFILES libtokyotyrant.so.$MYLIBVER.$MYLIBREV.0" MYLIBRARYFILES="$MYLIBRARYFILES libtokyotyrant.so.$MYLIBVER" MYLIBRARYFILES="$MYLIBRARYFILES libtokyotyrant.so" MYLIBRARYFILES="$MYLIBRARYFILES ttskelmock.so ttskeldir.so ttskelproxy.so" MYLIBRARYFILES="$MYLIBRARYFILES ttskelnull.so" fi fi #================================================================ # Generic Settings #================================================================ # Export variables AC_SUBST(MYLIBVER) AC_SUBST(MYLIBREV) AC_SUBST(MYPROTVER) AC_SUBST(MYHEADERFILES) AC_SUBST(MYLIBRARYFILES) AC_SUBST(MYLIBOBJFILES) AC_SUBST(MYCOMMANDFILES) AC_SUBST(MYINITFILES) AC_SUBST(MYMAN1FILES) AC_SUBST(MYMAN3FILES) AC_SUBST(MYMAN8FILES) AC_SUBST(MYSCREXTFILES) AC_SUBST(MYDOCUMENTFILES) AC_SUBST(MYPCFILES) AC_SUBST(MYCFLAGS) AC_SUBST(MYCPPFLAGS) AC_SUBST(MYLDFLAGS) AC_SUBST(MYCMDLDFLAGS) AC_SUBST(MYRUNPATH) AC_SUBST(MYLDLIBPATHENV) AC_SUBST(MYPOSTCMD) # Targets AC_OUTPUT(Makefile tokyotyrant.pc) # Messages printf '#================================================================\n' printf '# Ready to make.\n' printf '#================================================================\n' # END OF FILE tokyotyrant-1.1.40/doc/000077500000000000000000000000001133351147200147515ustar00rootroot00000000000000tokyotyrant-1.1.40/doc/common.css000066400000000000000000000056611133351147200167630ustar00rootroot00000000000000/* * Style Sheets commonly used by documents of Tokyo Cabinet */ html { margin: 0em 0em; padding: 0em 0em; background: #eeeeee none; } body { margin: 1em 2em; padding: 0em 0em; background: #eeeeee none; color: #111111; } hr { margin: 2.5em 0em 1.5em 0em; height: 1px; border: none; background: #999999 none; color: #999999; } h1,h2,h3,h4,h5,h6 { font-weight: bold; } h1 { margin: 1.0em 0em 1.3em 0em; padding: 0em 0em; font-size: 180%; color: #000000; } h2 { margin: 1.0em 0em 1.0em 0.2em; padding: 0.5em 0.5em; width: 60%; border-left: solid 0.6em #445555; border-bottom: solid 1px #bbbbbb; font-size: 150%; color: #000011; } h3 { margin: 0.8em 0em 0.5em 0.2em; padding: 0em 0em; font-size: 120%; color: #001111; } p { margin: 0.8em 0em; line-height: 140%; text-indent: 0.8em; } div,pre,table { margin: 0.8em 1.5em; } div.note,div.navi { text-align: right; margin: 0em 0.5em; color: #333333; } span.void { color: #888888; } div.logo { text-align: center; margin: 3em 0em; } div.logo img { border: inset 2px #ccccdd; } div.illust { margin: 1em 0em; text-align: center; } div.illust img { border: solid 1px #ccccdd; } pre { padding: 0.2em; background-color: #ddddee; border: 1px solid #bbbbcc; font-size: 95%; } li,dt,dd { line-height: 130%; } dt { margin-left: 1.2em; } dd { margin-left: 2.5em; text-indent: -0.3em; } dl.api { margin-top: -0.2em; } dl.api dd { margin-left: 3.0em; font-size: 95%; color: #333333; } ul { margin: 0.5em 2.0em; padding: 0em; } ul.options { list-style-type: none; margin: 0.5em 1.5em; font-size: 95%; color: #333333; } ul ul { margin-top: 0em; margin-bottom: 0em; } table { border-collapse: collapse; } td { text-align: left; vertical-align: top; padding: 0.1em 0.5em; border: solid 1px #aaaabb; font-size: 95%; } td.label { border: none; font-size: 80%; color: #333333; } td.number { text-align: right; } a { color: #0022aa; text-decoration: none; } a:hover,a:focus { color: #0033ee; text-decoration: underline; } code,kbd { font-style: normal; font-weight: bold; font-size: 100%; color: #001111; } var { padding: 0em 0.15em 0em 0em; font-style: italic; color: #001122; } @media print { html,body { margin: 0em 0em; background-color: #ffffff; color: #000000; } h1 { padding: 8em 0em 0.5em 0em; text-align: center; } h2 { page-break-before: always; } div.note { text-align: center; } div.navi,div.logo { display: none; } hr { display: none; } pre { margin: 0.8em 0.8em; background-color: #ffffff; border: 1px solid #aaaaaa; font-size: 90%; } a,code,kbd { color: #000000; text-decoration: none; } h1,h2,h3 { font-family: sans-serif; } p,div,li,dt,dd { font-family: serif; } pre,code,kbd { font-family: monospace; } dd { font-size: 90%; } } /* END OF FILE */ tokyotyrant-1.1.40/doc/index.html000066400000000000000000000054761133351147200167620ustar00rootroot00000000000000 Tokyo Tyrant: network interface of Tokyo Cabinet

Tokyo Tyrant: network interface of Tokyo Cabinet

Copyright (C) 2007-2010 Mikio Hirabayashi
Last Update: Wed, 20 Jan 2010 10:10:40 +0900

Overview

Tokyo Tyrant is a package of network interface to the DBM called Tokyo Cabinet. Though the DBM has high performance, you might bother in case that multiple processes share the same database, or remote processes access the database. Thus, Tokyo Tyrant is provided for concurrent and remote connections to Tokyo Cabinet. It is composed of the server process managing a database and its access library for client applications.

Tokyo Tyrant is written in the C language, and provided as API of C, Perl, and Ruby. Tokyo Tyrant is available on platforms which have API conforming to C99 and POSIX. Tokyo Tyrant is a free software licensed under the GNU Lesser General Public License.


Documents

The following are documents of Tokyo Tyrant. They are contained also in the source package.


Packages

The following are the source packages of Tokyo Tyrant. As for binary packages, see the site of each distributor.


Information

Tokyo Tyrant was written and is maintained by Mikio Hirabayashi. You can contact the author by e-mail to `hirarin@gmail.com'.


tokyotyrant-1.1.40/doc/spex.html000066400000000000000000003666061133351147200166370ustar00rootroot00000000000000 Fundamental Specifications of Tokyo Tyrant Version 1

Fundamental Specifications of Tokyo Tyrant Version 1

Copyright (C) 2007-2010 Mikio Hirabayashi
Last Update: Wed, 20 Jan 2010 10:10:40 +0900

Table of Contents

  1. Introduction
  2. Installation
  3. Server Programs
  4. Client Programs
  5. Remote Database API
  6. Lua Extension
  7. Protocol
  8. Tutorial
  9. License

Introduction

Tokyo Tyrant is a package of network interface to the DBM called Tokyo Cabinet. Though the DBM has high performance, you might bother in case that multiple processes share the same database, or remote processes access the database. Thus, Tokyo Tyrant is provided for concurrent and remote connections to Tokyo Cabinet. It is composed of the server process managing a database and its access library for client applications.

The server features high concurrency due to thread-pool modeled implementation and the epoll/kqueue mechanism of the modern Linux/*BSD kernel. The server and its clients communicate with each other by simple binary protocol on TCP/IP. Protocols compatible with memcached and HTTP are also supported so that almost all principal platforms and programming languages can use Tokyo Tyrant. High availability and high integrity are also featured due to such mechanisms as hot backup, update logging, and replication. The server can embed Lua, a lightweight script language so that you can define arbitrary operations of the database.

Because the server uses the abstract API of Tokyo Cabinet, all of the six APIs: the on-memory hash database API, the on-memory tree database API, the hash API, the B+ tree database API, the fixed-length database API, and the table database API, are available from the client with the common interface. Moreover, the table extension is provided to use specifidc features of the table database.

As for now, the server works on Linux, FreeBSD, Mac OS X, Solaris only.


Installation

Install the latest version of Tokyo Cabinet beforehand and get the package of Tokyo Tyrant.

When an archive file of Tokyo Tyrant is extracted, change the current working directory to the generated directory and perform installation.

Run the configuration script. To enable the Lua extension, add the `--enable-lua' option.

./configure

Build programs.

make

Install programs. This operation must be carried out by the root user.

make install

When a series of work finishes, the following files will be installed.

/usr/local/include/ttutil.h
/usr/local/include/tculog.h
/usr/local/include/tcrdb.h
/usr/local/lib/libtokyotyrant.a
/usr/local/lib/libtokyotyrant.so.x.y.z
/usr/local/lib/libtokyotyrant.so.x
/usr/local/lib/libtokyotyrant.so
/usr/local/lib/ttskelmock.so
/usr/local/lib/ttskeldir.so
/usr/local/lib/ttskelproxy.so
/usr/local/lib/ttskelnull.so
/usr/local/lib/pkgconfig/tokyotyrant.pc
/usr/local/bin/ttserver
/usr/local/bin/ttultest
/usr/local/bin/ttulmgr
/usr/local/bin/tcrtest
/usr/local/bin/tcrmttest
/usr/local/bin/tcrmgr
/usr/local/sbin/ttservctl
/usr/local/share/tokyotyrant/...
/usr/local/man/man1/...
/usr/local/man/man3/...

To test the server, perform the following command. To finish it, press Ctrl-C on the terminal.

ttserver

To test the client connecting to the above running server, perform the following command on another terminal.

make check

Server Programs

ttserver

The command `ttserver' runs the server managing a database instance. Because the database is treated by the abstract API of Tokyo Cabinet, you can choose the scheme on start-up of the server. Supported schema are on-memory hash database, on-memory tree database, hash database, and B+ tree database. This command is used in the following format. `dbname' specifies the database name. If it is omitted, on-memory hash database is specified.

ttserver [-host name] [-port num] [-thnum num] [-tout num] [-dmn] [-pid path] [-kl] [-log path] [-ld|-le] [-ulog path] [-ulim num] [-uas] [-sid num] [-mhost name] [-mport num] [-rts path] [-rcc] [-skel name] [-mul num] [-ext path] [-extpc name period] [-mask expr] [-unmask expr] [dbname]

Options feature the following.

  • -host name : specify the host name or the address of the server. By default, every network address is bound.
  • -port num : specify the port number. By default, it is 1978.
  • -thnum num : specify the number of worker threads. By default, it is 8.
  • -tout num : specify the timeout of each session in seconds. By default, no timeout is specified.
  • -dmn : work as a daemon process.
  • -pid path : output the process ID into the file.
  • -kl : kill the existing process if the process ID file is detected.
  • -log path : output log messages into the file.
  • -ld : log debug messages also.
  • -le : log error messages only.
  • -ulog path : specify the update log directory.
  • -ulim num : specify the limit size of each update log file.
  • -uas : use asynchronous I/O for the update log.
  • -sid num : specify the server ID.
  • -mhost name : specify the host name of the replication master server.
  • -mport num : specify the port number of the replication master server.
  • -rts path : specify the replication time stamp file.
  • -rcc : check consistency of replication.
  • -skel name : specify the name of the skeleton database library.
  • -mul num : specify the division number of the multiple database mechanism.
  • -ext path : specify the script language extension file.
  • -extpc name period : specify the function name and the calling period of a periodic command.
  • -mask expr : specify the names of forbidden commands.
  • -unmask expr : specify the names of allowed commands.

To terminate the server normally, send SIGINT or SIGTERM to the process. It is okay to press Ctrl-C on the controlling terminal. To restart the server, send SIGHUP to the process. If the port number is not more than 0, UNIX domain socket is used and the path of the socket file is specified by the host parameter. This command returns 0 on success, another on failure.

The naming convention of the database is specified by the abstract API of Tokyo Cabinet. If the name is "*", the database will be an on-memory hash database. If it is "+", the database will be an on-memory tree database. If its suffix is ".tch", the database will be a hash database. If its suffix is ".tcb", the database will be a B+ tree database. If its suffix is ".tcf", the database will be a fixed-length database. If its suffix is ".tct", the database will be a table database. Otherwise, this function fails. Tuning parameters can trail the name, separated by "#". Each parameter is composed of the name and the value, separated by "=". On-memory hash database supports "bnum", "capnum", and "capsiz". On-memory tree database supports "capnum" and "capsiz". Hash database supports "mode", "bnum", "apow", "fpow", "opts", "rcnum", "xmsiz", and "dfunit". B+ tree database supports "mode", "lmemb", "nmemb", "bnum", "apow", "fpow", "opts", "lcnum", "ncnum", "xmsiz", and "dfunit". Fixed-length database supports "mode", "width", and "limsiz". Table database supports "mode", "bnum", "apow", "fpow", "opts", "rcnum", "lcnum", "ncnum", "xmsiz", "dfunit", and "idx". The tuning parameter "capnum" specifies the capacity number of records. "capsiz" specifies the capacity size of using memory. Records spilled the capacity are removed by the storing order. "mode" can contain "w" of writer, "r" of reader, "c" of creating, "t" of truncating, "e" of no locking, and "f" of non-blocking lock. The default mode is relevant to "wc". "opts" can contains "l" of large option, "d" of Deflate option, "b" of BZIP2 option, and "t" of TCBS option. "idx" specifies the column name of an index and its type separated by ":". For example, "casket.tch#bnum=1000000#opts=ld" means that the name of the database file is "casket.tch", and the bucket number is 1000000, and the options are large and Deflate.

The command mask expression is a list of command names separated by ",". For example, "out,vanish,copy" means a set of "out", "vanish", and "copy". Commands of the memcached compatible protocol and the HTTP compatible protocol are also forbidden or allowed, related by the mask of each original command. Moreover, there are meta expressions. "all" means all commands. "allorg" means all commands of the original binary protocol. "allmc" means all commands of the memcached compatible protocol. "allhttp" means all commands of the HTTP compatible protocol. "allread" is the abbreviation of `get', `mget', `vsiz', `iterinit', `iternext', `fwmkeys', `rnum', `size', and `stat'. "allwrite" is the abbreviation of `put', `putkeep', `putcat', `putshl', `putnr', `out', `addint', `adddouble', `vanish', and `misc'. "allmanage" is the abbreviation of `sync', `optimize', `copy', `restore', and `setmst'. "repl" means replication as master. "slave" means replication as slave.

ttservctl

The command `ttservctl' is the startup script of the server. It can be called by the RC script of the bootstrap process of the operating system. This command is used in the following format.

ttservctl start
Startup the server.
ttservctl stop
Stop the server.
ttservctl restart
Restart the server.
ttservctl hup
Send HUP signal to the server for log rotation.

The database is placed as "/var/ttserver/casket.tch". The log and related files are also placed in "/var/ttserver". This command returns 0 on success, another on failure.

ttulmgr

The command `ttulmgr' is the utility to export and import the update log. It is useful to filter the update log with such text utilities as `grep' and `sed'. This command is used in the following format. `upath' specifies the update log directory.

ttulmgr export [-ts num] [-sid num] upath
Export the update log as TSV text data to the standard output.
ttulmgr import upath
Import TSV text data from the standard input to the update log.

Options feature the following.

  • -ts num : specify the beginning time stamp.
  • -sid num : specify the self server ID.

This command returns 0 on success, another on failure.


Client Programs

tcrtest

The command `tcrtest' is a utility for facility test and performance test. This command is used in the following format. `host' specifies the host name of the server. `rnum' specifies the number of iterations.

tcrtest write [-port num] [-cnum num] [-tout num] [-nr] [-rnd] host rnum
Store records with keys of 8 bytes. They change as `00000001', `00000002'...
tcrtest read [-port num] [-cnum num] [-tout num] [-mul num] [-rnd] host
Retrieve all records of the database above.
tcrtest remove [-port num] [-cnum num] [-tout num] [-rnd] host
Remove all records of the database above.
tcrtest rcat [-port num] [-cnum num] [-tout num] [-shl num] [-dai|-dad] [-ext name] [-xlr|-xlg] host rnum
Store records with partway duplicated keys using concatenate mode.
tcrtest misc [-port num] [-cnum num] [-tout num] host rnum
Perform miscellaneous test of various operations.
tcrtest wicked [-port num] [-cnum num] [-tout num] host rnum
Perform updating operations of list and map selected at random.
tcrtest table [-port num] [-cnum num] [-tout num] [-exp num] host rnum
Perform miscellaneous test of the table extension.

Options feature the following.

  • -port num : specify the port number.
  • -cnum num : specify the number of connections.
  • -tout num : specify the timeout of each session in seconds.
  • -nr : use the function `tcrdbputnr' instead of `tcrdbput'.
  • -rnd : select keys at random.
  • -mul num : specify the number of records for the mget command.
  • -shl num : use `tcrdbputshl' and specify the width.
  • -dai : use `tcrdbaddint' instead of `tcrdbputcat'.
  • -dad : use `tcrdbadddouble' instead of `tcrdbputcat'.
  • -ext name : call a script language extension function.
  • -xlr : perform record locking.
  • -xlg : perform global locking.
  • -exp num : specify the lifetime of expiration test.

If the port number is not more than 0, UNIX domain socket is used and the path of the socket file is specified by the host parameter. This command returns 0 on success, another on failure.

tcrmttest

The command `tcrmttest' is a utility for facility test under multi-thread situation. This command is used in the following format. `host' specifies the host name of the server. `rnum' specifies the number of iterations.

tcrmttest write [-port num] [-tnum num] [-nr] [-rnd] [-ext name] host rnum
Store records with keys of 8 bytes. They change as `00000001', `00000002'...
tcrmttest read [-port num] [-tnum num] [-mul num] host
Retrieve all records of the database above.
tcrmttest remove [-port num] [-tnum num] host
Remove all records of the database above.

Options feature the following.

  • -port num : specify the port number.
  • -tnum num : specify the number of running threads.
  • -nr : use the function `tcrdbputnr' instead of `tcrdbput'.
  • -rnd : select keys at random.
  • -ext name : call a script language extension function.
  • -mul num : specify the number of records for the mget command.

If the port number is not more than 0, UNIX domain socket is used and the path of the socket file is specified by the host parameter. This command returns 0 on success, another on failure.

tcrmgr

The command `tcrmgr' is a utility for test and debugging of the remote database API and its applications. `host' specifies the host name of the server. `key' specifies the key of a record. `value' specifies the value of a record. `params' specifies the tuning parameters. `dpath' specifies the destination file. `func specifies the name of the function. `arg' specifies the arguments of the function. `file' specifies the input file. `upath' specifies the update log directory. `mhost' specifies the host name of the replication master. `url' specifies the target URL.

tcrmgr inform [-port num] [-st] host
Print miscellaneous information to the standard output.
tcrmgr put [-port num] [-sx] [-sep chr] [-dk|-dc|-dai|-dad] host key value
Store a record.
tcrmgr out [-port num] [-sx] [-sep chr] host key
Remove a record.
tcrmgr get [-port num] [-sx] [-sep chr] [-px] [-pz] host key
Print the value of a record.
tcrmgr mget [-port num] [-sx] [-sep chr] [-px] host [key...]
Print keys and values of multiple records.
tcrmgr list [-port num] [-sep chr] [-m num] [-pv] [-px] [-fm str] host
Print keys of all records, separated by line feeds.
tcrmgr ext [-port num] [-xlr|-xlg] [-sx] [-sep chr] [-px] host func [key [value]]
Call a script language extension function.
tcrmgr sync [-port num] host
Synchronize updated contents with the database file.
tcrmgr optimize [-port num] host [params]
Optimize the database file.
tcrmgr vanish [-port num] host
Remove all records.
tcrmgr copy [-port num] host dpath
Copy the database file.
tcrmgr misc [-port num] [-mnu] [-sx] [-sep chr] [-px] host func [arg...]
Call a versatile function for miscellaneous operations.
tcrmgr importtsv [-port num] [-nr] [-sc] host [file]
Store records of TSV in each line of a file.
tcrmgr restore [-port num] [-ts num] [-rcc] host upath
Restore the database with update log.
tcrmgr setmst [-port num] [-mport num] [-ts num] [-rcc] host [mhost]
Set the replication master.
tcrmgr repl [-port num] [-ts num] [-sid num] [-ph] host
Replicate the update log.
tcrmgr http [-ah name value] [-ih] url
Fetch the resource of a URL by HTTP.
tcrmgr version
Print the version information of Tokyo Tyrant.

Options feature the following.

  • -port num : specify the port number.
  • -st : print miscellaneous status data.
  • -sx : input data is evaluated as a hexadecimal data string.
  • -sep chr : specify the separator of the input data.
  • -dk : use the function `tcrdbputkeep' instead of `tcrdbput'.
  • -dc : use the function `tcrdbputcat' instead of `tcrdbput'.
  • -dai : use the function `tcrdbaddint' instead of `tcrdbput'.
  • -dad : use the function `tcrdbadddouble' instead of `tcrdbput'.
  • -px : output data is converted into a hexadecimal data string.
  • -pz : do not append line feed at the end of the output.
  • -m num : specify the maximum number of the output.
  • -pv : print values of records also.
  • -fm str : specify the prefix of keys.
  • -xlr : perform record locking.
  • -xlg : perform global locking.
  • -mnu : omit the update log.
  • -nr : use the function `tcrdbputnr' instead of `tcrdbput'.
  • -sc : normalize keys as lower cases.
  • -mport num : specify the port number of the replication master.
  • -ts num : specify the beginning time stamp.
  • -rcc : check consistency of replication.
  • -sid num : specify the self server ID.
  • -ph : print human-readable data.
  • -ah name value : add a request header.
  • -ih : output response headers also.

If the port number is not more than 0, UNIX domain socket is used and the path of the socket file is specified by the host parameter. This command returns 0 on success, another on failure.


Remote Database API

Remote database is a set of interfaces to use an abstract database of Tokyo Cabinet, mediated by a server of Tokyo Tyrant. See `tcrdb.h' for entire specification.

Description

To use the remote database API, include `tcrdb.h' and related standard header files. Usually, write the following description near the front of a source file.

#include <tcrdb.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdint.h>

Objects whose type is pointer to `TCRDB' are used to handle remote databases. A remote database object is created with the function `tcrdbnew' and is deleted with the function `tcrdbdel'. To avoid memory leak, it is important to delete every object when it is no longer in use.

Before operations to store or retrieve records, it is necessary to connect the remote database object to the server. The function `tcrdbopen' is used to open a database connection and the function `tcrdbclose' is used to close the connection.

API

The function `tcrdberrmsg' is used in order to get the message string corresponding to an error code.

const char *tcrdberrmsg(int ecode);
`ecode' specifies the error code.
The return value is the message string of the error code.

The function `tcrdbnew' is used in order to create a remote database object.

TCRDB *tcrdbnew(void);
The return value is the new remote database object.

The function `tcrdbdel' is used in order to delete a remote database object.

void tcrdbdel(TCRDB *rdb);
`rdb' specifies the remote database object.

The function `tcrdbecode' is used in order to get the last happened error code of a remote database object.

int tcrdbecode(TCRDB *rdb);
`rdb' specifies the remote database object.
The return value is the last happened error code.
The following error code is defined: `TTESUCCESS' for success, `TTEINVALID' for invalid operation, `TTENOHOST' for host not found, `TTEREFUSED' for connection refused, `TTESEND' for send error, `TTERECV' for recv error, `TTEKEEP' for existing record, `TTENOREC' for no record found, `TTEMISC' for miscellaneous error.

The function `tcrdbtune' is used in order to set the tuning parameters of a hash database object.

bool tcrdbtune(TCRDB *rdb, double timeout, int opts);
`rdb' specifies the remote database object.
`timeout' specifies the timeout of each query in seconds. If it is not more than 0, the timeout is not specified.
`opts' specifies options by bitwise-or: `RDBTRECON' specifies that the connection is recovered automatically when it is disconnected.
If successful, the return value is true, else, it is false.
Note that the tuning parameters should be set before the database is opened.

The function `tcrdbopen' is used in order to open a remote database.

bool tcrdbopen(TCRDB *rdb, const char *host, int port);
`rdb' specifies the remote database object.
`host' specifies the name or the address of the server.
`port' specifies the port number. If it is not more than 0, UNIX domain socket is used and the path of the socket file is specified by the host parameter.
If successful, the return value is true, else, it is false.

The function `tcrdbopen2' is used in order to open a remote database with a simple server expression.

bool tcrdbopen2(TCRDB *rdb, const char *expr);
`rdb' specifies the remote database object.
`expr' specifies the simple server expression. It is composed of two substrings separated by ":". The former field specifies the name or the address of the server. The latter field specifies the port number. If the latter field is omitted, the default port number is specified.
If successful, the return value is true, else, it is false.

The function `tcrdbclose' is used in order to close a remote database object.

bool tcrdbclose(TCRDB *rdb);
`rdb' specifies the remote database object.
If successful, the return value is true, else, it is false.

The function `tcrdbput' is used in order to store a record into a remote database object.

bool tcrdbput(TCRDB *rdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
`rdb' specifies the remote database object.
`kbuf' specifies the pointer to the region of the key.
`ksiz' specifies the size of the region of the key.
`vbuf' specifies the pointer to the region of the value.
`vsiz' specifies the size of the region of the value.
If successful, the return value is true, else, it is false.
If a record with the same key exists in the database, it is overwritten.

The function `tcrdbput2' is used in order to store a string record into a remote object.

bool tcrdbput2(TCRDB *rdb, const char *kstr, const char *vstr);
`rdb' specifies the remote database object.
`kstr' specifies the string of the key.
`vstr' specifies the string of the value.
If successful, the return value is true, else, it is false.
If a record with the same key exists in the database, it is overwritten.

The function `tcrdbputkeep' is used in order to store a new record into a remote database object.

bool tcrdbputkeep(TCRDB *rdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
`rdb' specifies the remote database object.
`kbuf' specifies the pointer to the region of the key.
`ksiz' specifies the size of the region of the key.
`vbuf' specifies the pointer to the region of the value.
`vsiz' specifies the size of the region of the value.
If successful, the return value is true, else, it is false.
If a record with the same key exists in the database, this function has no effect.

The function `tcrdbputkeep2' is used in order to store a new string record into a remote database object.

bool tcrdbputkeep2(TCRDB *rdb, const char *kstr, const char *vstr);
`rdb' specifies the remote database object.
`kstr' specifies the string of the key.
`vstr' specifies the string of the value.
If successful, the return value is true, else, it is false.
If a record with the same key exists in the database, this function has no effect.

The function `tcrdbputcat' is used in order to concatenate a value at the end of the existing record in a remote database object.

bool tcrdbputcat(TCRDB *rdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
`rdb' specifies the remote database object.
`kbuf' specifies the pointer to the region of the key.
`ksiz' specifies the size of the region of the key.
`vbuf' specifies the pointer to the region of the value.
`vsiz' specifies the size of the region of the value.
If successful, the return value is true, else, it is false.
If there is no corresponding record, a new record is created.

The function `tcrdbputcat2' is used in order to concatenate a string value at the end of the existing record in a remote database object.

bool tcrdbputcat2(TCRDB *rdb, const char *kstr, const char *vstr);
`rdb' specifies the remote database object.
`kstr' specifies the string of the key.
`vstr' specifies the string of the value.
If successful, the return value is true, else, it is false.
If there is no corresponding record, a new record is created.

The function `tcrdbputshl' is used in order to concatenate a value at the end of the existing record and shift it to the left.

bool tcrdbputshl(TCRDB *rdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz, int width);
`rdb' specifies the remote database object.
`kbuf' specifies the pointer to the region of the key.
`ksiz' specifies the size of the region of the key.
`vbuf' specifies the pointer to the region of the value.
`vsiz' specifies the size of the region of the value.
`width' specifies the width of the record.
If successful, the return value is true, else, it is false.
If there is no corresponding record, a new record is created.

The function `tcrdbputshl2' is used in order to concatenate a string value at the end of the existing record and shift it to the left.

bool tcrdbputshl2(TCRDB *rdb, const char *kstr, const char *vstr, int width);
`rdb' specifies the remote database object.
`kstr' specifies the string of the key.
`vstr' specifies the string of the value.
`width' specifies the width of the record.
If successful, the return value is true, else, it is false.
If there is no corresponding record, a new record is created.

The function `tcrdbputnr' is used in order to store a record into a remote database object without response from the server.

bool tcrdbputnr(TCRDB *rdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
`rdb' specifies the remote database object.
`kbuf' specifies the pointer to the region of the key.
`ksiz' specifies the size of the region of the key.
`vbuf' specifies the pointer to the region of the value.
`vsiz' specifies the size of the region of the value.
If successful, the return value is true, else, it is false.
If a record with the same key exists in the database, it is overwritten.

The function `tcrdbputnr2' is used in order to store a string record into a remote object without response from the server.

bool tcrdbputnr2(TCRDB *rdb, const char *kstr, const char *vstr);
`rdb' specifies the remote database object.
`kstr' specifies the string of the key.
`vstr' specifies the string of the value.
If successful, the return value is true, else, it is false.
If a record with the same key exists in the database, it is overwritten.

The function `tcrdbout' is used in order to remove a record of a remote database object.

bool tcrdbout(TCRDB *rdb, const void *kbuf, int ksiz);
`rdb' specifies the remote database object.
`kbuf' specifies the pointer to the region of the key.
`ksiz' specifies the size of the region of the key.
If successful, the return value is true, else, it is false.

The function `tcrdbout2' is used in order to remove a string record of a remote database object.

bool tcrdbout2(TCRDB *rdb, const char *kstr);
`rdb' specifies the remote database object.
`kstr' specifies the string of the key.
If successful, the return value is true, else, it is false.

The function `tcrdbget' is used in order to retrieve a record in a remote database object.

void *tcrdbget(TCRDB *rdb, const void *kbuf, int ksiz, int *sp);
`rdb' specifies the remote database object.
`kbuf' specifies the pointer to the region of the key.
`ksiz' specifies the size of the region of the key.
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
If successful, the return value is the pointer to the region of the value of the corresponding record. `NULL' is returned if no record corresponds.
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.

The function `tcrdbget2' is used in order to retrieve a string record in a remote database object.

char *tcrdbget2(TCRDB *rdb, const char *kstr);
`rdb' specifies the remote database object.
`kstr' specifies the string of the key.
If successful, the return value is the string of the value of the corresponding record. `NULL' is returned if no record corresponds.
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.

The function `tcrdbget3' is used in order to retrieve records in a remote database object.

bool tcrdbget3(TCRDB *rdb, TCMAP *recs);
`rdb' specifies the remote database object.
`recs' specifies a map object containing the retrieval keys. As a result of this function, keys existing in the database have the corresponding values and keys not existing in the database are removed.
If successful, the return value is true, else, it is false.

The function `tcrdbvsiz' is used in order to get the size of the value of a record in a remote database object.

int tcrdbvsiz(TCRDB *rdb, const void *kbuf, int ksiz);
`rdb' specifies the remote database object.
`kbuf' specifies the pointer to the region of the key.
`ksiz' specifies the size of the region of the key.
If successful, the return value is the size of the value of the corresponding record, else, it is -1.

The function `tcrdbvsiz2' is used in order to get the size of the value of a string record in a remote database object.

int tcrdbvsiz2(TCRDB *rdb, const char *kstr);
`rdb' specifies the remote database object.
`kstr' specifies the string of the key.
If successful, the return value is the size of the value of the corresponding record, else, it is -1.

The function `tcrdbiterinit' is used in order to initialize the iterator of a remote database object.

bool tcrdbiterinit(TCRDB *rdb);
`rdb' specifies the remote database object.
If successful, the return value is true, else, it is false.
The iterator is used in order to access the key of every record stored in a database.

The function `tcrdbiternext' is used in order to get the next key of the iterator of a remote database object.

void *tcrdbiternext(TCRDB *rdb, int *sp);
`rdb' specifies the remote database object.
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
If successful, the return value is the pointer to the region of the next key, else, it is `NULL'. `NULL' is returned when no record is to be get out of the iterator.
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use. The iterator can be updated by multiple connections and then it is not assured that every record is traversed.

The function `tcrdbiternext2' is used in order to get the next key string of the iterator of a remote database object.

char *tcrdbiternext2(TCRDB *rdb);
`rdb' specifies the remote database object.
If successful, the return value is the string of the next key, else, it is `NULL'. `NULL' is returned when no record is to be get out of the iterator.
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use. The iterator can be updated by multiple connections and then it is not assured that every record is traversed.

The function `tcrdbfwmkeys' is used in order to get forward matching keys in a remote database object.

TCLIST *tcrdbfwmkeys(TCRDB *rdb, const void *pbuf, int psiz, int max);
`rdb' specifies the remote database object.
`pbuf' specifies the pointer to the region of the prefix.
`psiz' specifies the size of the region of the prefix.
`max' specifies the maximum number of keys to be fetched. If it is negative, no limit is specified.
The return value is a list object of the corresponding keys. This function does never fail. It returns an empty list even if no key corresponds.
Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use.

The function `tcrdbfwmkeys2' is used in order to get forward matching string keys in a remote database object.

TCLIST *tcrdbfwmkeys2(TCRDB *rdb, const char *pstr, int max);
`rdb' specifies the remote database object.
`pstr' specifies the string of the prefix.
`max' specifies the maximum number of keys to be fetched. If it is negative, no limit is specified.
The return value is a list object of the corresponding keys. This function does never fail. It returns an empty list even if no key corresponds.
Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use.

The function `tcrdbaddint' is used in order to add an integer to a record in a remote database object.

int tcrdbaddint(TCRDB *rdb, const void *kbuf, int ksiz, int num);
`rdb' specifies the remote database object.
`kbuf' specifies the pointer to the region of the key.
`ksiz' specifies the size of the region of the key.
`num' specifies the additional value.
If successful, the return value is the summation value, else, it is `INT_MIN'.
If the corresponding record exists, the value is treated as an integer and is added to. If no record corresponds, a new record of the additional value is stored.

The function `tcrdbadddouble' is used in order to add a real number to a record in a remote database object.

double tcrdbadddouble(TCRDB *rdb, const void *kbuf, int ksiz, double num);
`rdb' specifies the remote database object.
`kbuf' specifies the pointer to the region of the key.
`ksiz' specifies the size of the region of the key.
`num' specifies the additional value.
If successful, the return value is the summation value, else, it is Not-a-Number.
If the corresponding record exists, the value is treated as a real number and is added to. If no record corresponds, a new record of the additional value is stored.

The function `tcrdbext' is used in order to call a function of the script language extension.

void *tcrdbext(TCRDB *rdb, const char *name, int opts, const void *kbuf, int ksiz, const void *vbuf, int vsiz, int *sp);
`rdb' specifies the remote database object.
`name' specifies the function name.
`opts' specifies options by bitwise-or: `RDBXOLCKREC' for record locking, `RDBXOLCKGLB' for global locking.
`kbuf' specifies the pointer to the region of the key.
`ksiz' specifies the size of the region of the key.
`vbuf' specifies the pointer to the region of the value.
`vsiz' specifies the size of the region of the value.
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
If successful, the return value is the pointer to the region of the value of the response. `NULL' is returned on failure.
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.

The function `tcrdbext2' is used in order to call a function of the script language extension with string parameters.

char *tcrdbext2(TCRDB *rdb, const char *name, int opts, const char *kstr, const char *vstr);
`rdb' specifies the remote database object.
`name' specifies the function name.
`opts' specifies options by bitwise-or: `RDBXOLCKREC' for record locking, `RDBXOLCKGLB' for global locking.
`kstr' specifies the string of the key.
`vstr' specifies the string of the value.
If successful, the return value is the string of the value of the response. `NULL' is returned on failure.
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.

The function `tcrdbsync' is used in order to synchronize updated contents of a remote database object with the file and the device.

bool tcrdbsync(TCRDB *rdb);
`rdb' specifies the remote database object.
If successful, the return value is true, else, it is false.

The function `tcrdboptimize' is used in order to optimize the storage of a remove database object.

bool tcrdboptimize(TCRDB *rdb, const char *params);
`rdb' specifies the remote database object.
`params' specifies the string of the tuning parameters. If it is `NULL', it is not used.
If successful, the return value is true, else, it is false.

The function `tcrdbvanish' is used in order to remove all records of a remote database object.

bool tcrdbvanish(TCRDB *rdb);
`rdb' specifies the remote database object.
If successful, the return value is true, else, it is false.

The function `tcrdbcopy' is used in order to copy the database file of a remote database object.

bool tcrdbcopy(TCRDB *rdb, const char *path);
`rdb' specifies the remote database object.
`path' specifies the path of the destination file. If it begins with `@', the trailing substring is executed as a command line.
If successful, the return value is true, else, it is false. False is returned if the executed command returns non-zero code.
The database file is assured to be kept synchronized and not modified while the copying or executing operation is in progress. So, this function is useful to create a backup file of the database file.

The function `tcrdbrestore' is used in order to restore the database file of a remote database object from the update log.

bool tcrdbrestore(TCRDB *rdb, const char *path, uint64_t ts, int opts);
`rdb' specifies the remote database object.
`path' specifies the path of the update log directory.
`opts' specifies options by bitwise-or: `RDBROCHKCON' for consistency checking.
`ts' specifies the beginning time stamp in microseconds.
If successful, the return value is true, else, it is false.

The function `tcrdbsetmst' is used in order to set the replication master of a remote database object.

bool tcrdbsetmst(TCRDB *rdb, const char *host, int port, uint64_t ts, int opts);
`rdb' specifies the remote database object.
`host' specifies the name or the address of the server. If it is `NULL', replication of the database is disabled.
`port' specifies the port number.
`ts' specifies the beginning timestamp in microseconds.
`opts' specifies options by bitwise-or: `RDBROCHKCON' for consistency checking.
If successful, the return value is true, else, it is false.

The function `tcrdbsetmst2' is used in order to set the replication master of a remote database object with a simple server expression.

bool tcrdbsetmst2(TCRDB *rdb, const char *expr, uint64_t ts, int opts);
`rdb' specifies the remote database object.
`expr' specifies the simple server expression. It is composed of two substrings separated by ":". The former field specifies the name or the address of the server. The latter field specifies the port number. If the latter field is omitted, the default port number is specified.
`ts' specifies the beginning timestamp in microseconds.
`opts' specifies options by bitwise-or: `RDBROCHKCON' for consistency checking.
If successful, the return value is true, else, it is false.

The function `tcrdbrnum' is used in order to get the number of records of a remote database object.

uint64_t tcrdbrnum(TCRDB *rdb);
`rdb' specifies the remote database object.
The return value is the number of records or 0 if the object does not connect to any database server.

The function `tcrdbsize' is used in order to get the size of the database of a remote database object.

uint64_t tcrdbsize(TCRDB *rdb);
`rdb' specifies the remote database object.
The return value is the size of the database or 0 if the object does not connect to any database server.

The function `tcrdbstat' is used in order to get the status string of the database of a remote database object.

char *tcrdbstat(TCRDB *rdb);
`rdb' specifies the remote database object.
The return value is the status message of the database or `NULL' if the object does not connect to any database server. The message format is TSV. The first field of each line means the parameter name and the second field means the value.
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.

The function `tcrdbmisc' is used in order to call a versatile function for miscellaneous operations of a remote database object.

TCLIST *tcrdbmisc(TCRDB *rdb, const char *name, int opts, const TCLIST *args);
`rdb' specifies the remote database object.
`name' specifies the name of the function. All databases support "put", "out", "get", "putlist", "outlist", and "getlist". "put" is to store a record. It receives a key and a value, and returns an empty list. "out" is to remove a record. It receives a key, and returns an empty list. "get" is to retrieve a record. It receives a key, and returns a list of the values. "putlist" is to store records. It receives keys and values one after the other, and returns an empty list. "outlist" is to remove records. It receives keys, and returns an empty list. "getlist" is to retrieve records. It receives keys, and returns keys and values of corresponding records one after the other.
`opts' specifies options by bitwise-or: `RDBMONOULOG' for omission of the update log.
`args' specifies a list object containing arguments.
If successful, the return value is a list object of the result. `NULL' is returned on failure.
Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use.

API of the Table Extension

The function `tcrdbtblput' is used in order to store a record into a remote database object.

bool tcrdbtblput(TCRDB *rdb, const void *pkbuf, int pksiz, TCMAP *cols);
`rdb' specifies the remote database object.
`pkbuf' specifies the pointer to the region of the primary key.
`pksiz' specifies the size of the region of the primary key.
`cols' specifies a map object containing columns.
If successful, the return value is true, else, it is false.
If a record with the same key exists in the database, it is overwritten.

The function `tcrdbtblputkeep' is used in order to store a new record into a remote database object.

bool tcrdbtblputkeep(TCRDB *rdb, const void *pkbuf, int pksiz, TCMAP *cols);
`rdb' specifies the remote database object.
`pkbuf' specifies the pointer to the region of the primary key.
`pksiz' specifies the size of the region of the primary key.
`cols' specifies a map object containing columns.
If successful, the return value is true, else, it is false.
If a record with the same key exists in the database, this function has no effect.

The function `tcrdbtblputcat' is used in order to concatenate columns of the existing record in a remote database object.

bool tcrdbtblputcat(TCRDB *rdb, const void *pkbuf, int pksiz, TCMAP *cols);
`rdb' specifies the remote database object.
`pkbuf' specifies the pointer to the region of the primary key.
`pksiz' specifies the size of the region of the primary key.
`cols' specifies a map object containing columns.
If successful, the return value is true, else, it is false.
If there is no corresponding record, a new record is created.

The function `tcrdbtblout' is used in order to remove a record of a remote database object.

bool tcrdbtblout(TCRDB *rdb, const void *pkbuf, int pksiz);
`rdb' specifies the remote database object.
`pkbuf' specifies the pointer to the region of the primary key.
`pksiz' specifies the size of the region of the primary key.
If successful, the return value is true, else, it is false.

The function `tcrdbtblget' is used in order to retrieve a record in a remote database object.

TCMAP *tcrdbtblget(TCRDB *rdb, const void *pkbuf, int pksiz);
`rdb' specifies the remote database object.
`pkbuf' specifies the pointer to the region of the primary key.
`pksiz' specifies the size of the region of the primary key.
If successful, the return value is a map object of the columns of the corresponding record. `NULL' is returned if no record corresponds.
Because the object of the return value is created with the function `tcmapnew', it should be deleted with the function `tcmapdel' when it is no longer in use.

The function `tcrdbtblsetindex' is used in order to set a column index to a remote database object.

bool tcrdbtblsetindex(TCRDB *rdb, const char *name, int type);
`rdb' specifies the remote database object.
`name' specifies the name of a column. If the name of an existing index is specified, the index is rebuilt. An empty string means the primary key.
`type' specifies the index type: `RDBITLEXICAL' for lexical string, `RDBITDECIMAL' for decimal string, `RDBITTOKEN' for token inverted index, `RDBITQGRAM' for q-gram inverted index. If it is `RDBITOPT', the index is optimized. If it is `RDBITVOID', the index is removed. If `RDBITKEEP' is added by bitwise-or and the index exists, this function merely returns failure.
If successful, the return value is true, else, it is false.

The function `tcrdbtblgenuid' is used in order to generate a unique ID number of a remote database object.

int64_t tcrdbtblgenuid(TCRDB *rdb);
`rdb' specifies the remote database object.
The return value is the new unique ID number or -1 on failure.

The function `tcrdbqrynew' is used in order to create a query object.

RDBQRY *tcrdbqrynew(TCRDB *rdb);
`rdb' specifies the remote database object.
The return value is the new query object.

The function `tcrdbqrydel' is used in order to delete a query object.

void tcrdbqrydel(RDBQRY *qry);
`qry' specifies the query object.

The function `tcrdbqryaddcond' is used in order to add a narrowing condition to a query object.

void tcrdbqryaddcond(RDBQRY *qry, const char *name, int op, const char *expr);
`qry' specifies the query object.
`name' specifies the name of a column. An empty string means the primary key.
`op' specifies an operation type: `RDBQCSTREQ' for string which is equal to the expression, `RDBQCSTRINC' for string which is included in the expression, `RDBQCSTRBW' for string which begins with the expression, `RDBQCSTREW' for string which ends with the expression, `RDBQCSTRAND' for string which includes all tokens in the expression, `RDBQCSTROR' for string which includes at least one token in the expression, `RDBQCSTROREQ' for string which is equal to at least one token in the expression, `RDBQCSTRRX' for string which matches regular expressions of the expression, `RDBQCNUMEQ' for number which is equal to the expression, `RDBQCNUMGT' for number which is greater than the expression, `RDBQCNUMGE' for number which is greater than or equal to the expression, `RDBQCNUMLT' for number which is less than the expression, `RDBQCNUMLE' for number which is less than or equal to the expression, `RDBQCNUMBT' for number which is between two tokens of the expression, `RDBQCNUMOREQ' for number which is equal to at least one token in the expression, `RDBQCFTSPH' for full-text search with the phrase of the expression, `RDBQCFTSAND' for full-text search with all tokens in the expression, `RDBQCFTSOR' for full-text search with at least one token in the expression, `RDBQCFTSEX' for full-text search with the compound expression. All operations can be flagged by bitwise-or: `RDBQCNEGATE' for negation, `RDBQCNOIDX' for using no index.
`expr' specifies an operand exression.

The function `tcrdbqrysetorder' is used in order to set the order of a query object.

void tcrdbqrysetorder(RDBQRY *qry, const char *name, int type);
`qry' specifies the query object.
`name' specifies the name of a column. An empty string means the primary key.
`type' specifies the order type: `RDBQOSTRASC' for string ascending, `RDBQOSTRDESC' for string descending, `RDBQONUMASC' for number ascending, `RDBQONUMDESC' for number descending.

The function `tcrdbqrysetlimit' is used in order to set the limit number of records of the result of a query object.

void tcrdbqrysetlimit(RDBQRY *qry, int max, int skip);
`qry' specifies the query object.
`max' specifies the maximum number of records of the result. If it is negative, no limit is specified.
`skip' specifies the number of skipped records of the result. If it is not more than 0, no record is skipped.

The function `tcrdbqrysearch' is used in order to execute the search of a query object.

TCLIST *tcrdbqrysearch(RDBQRY *qry);
`qry' specifies the query object.
The return value is a list object of the primary keys of the corresponding records. This function does never fail. It returns an empty list even if no record corresponds.
Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use.

The function `tcrdbqrysearchout' is used in order to remove each record corresponding to a query object.

bool tcrdbqrysearchout(RDBQRY *qry);
`qry' specifies the query object of the database.
If successful, the return value is true, else, it is false.

The function `tcrdbqrysearchget' is used in order to get records corresponding to the search of a query object.

TCLIST *tcrdbqrysearchget(RDBQRY *qry);
`qry' specifies the query object.
The return value is a list object of zero separated columns of the corresponding records.
This function does never fail. It returns an empty list even if no record corresponds. Each element of the list can be treated with the function `tcrdbqryrescols'. Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use.

The function `tcrdbqryrescols' is used in order to get columns of a record in a search result.

TCMAP *tcrdbqryrescols(TCLIST *res, int index);
`res' specifies a list of zero separated columns of the search result.
`index' the index of a element of the search result.
The return value is a map object containing columns.
Because the object of the return value is created with the function `tcmapnew', it should be deleted with the function `tcmapdel' when it is no longer in use.

The function `tcrdbqrysearchcount' is used in order to get the count of corresponding records of a query object.

int tcrdbqrysearchcount(RDBQRY *qry);
`qry' specifies the query object.
The return value is the count of corresponding records or 0 on failure.

The function `tcrdbqryhint' is used in order to get the hint string of a query object.

const char *tcrdbqryhint(RDBQRY *qry);
`qry' specifies the query object.
The return value is the hint string.
This function should be called after the query execution by `tcrdbqrysearch' and so on. The region of the return value is overwritten when this function is called again.

The function `tcrdbmetasearch' is used in order to retrieve records with multiple query objects and get the set of the result.

TCLIST *tcrdbmetasearch(RDBQRY **qrys, int num, int type);
`qrys' specifies an array of the query objects.
`num' specifies the number of elements of the array.
`type' specifies a set operation type: `RDBMSUNION' for the union set, `RDBMSISECT' for the intersection set, `RDBMSDIFF' for the difference set.
The return value is a list object of the primary keys of the corresponding records. This function does never fail. It returns an empty list even if no record corresponds.
If the first query object has the order setting, the result array is sorted by the order. Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use.

The function `tcrdbparasearch' is used in order to search records for multiple servers in parallel.

TCLIST *tcrdbparasearch(RDBQRY **qrys, int num);
`qrys' specifies an array of the query objects.
`num' specifies the number of elements of the array.
The return value is a list object of zero separated columns of the corresponding records.
This function does never fail. It returns an empty list even if no record corresponds. Each element of the list can be treated with the function `tcrdbqryrescols'. Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use.

Example Code

The following code is an example to use a remote database.

#include <tcrdb.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdint.h>

int main(int argc, char **argv){

  TCRDB *rdb;
  int ecode;
  char *value;

  /* create the object */
  rdb = tcrdbnew();

  /* connect to the server */
  if(!tcrdbopen(rdb, "localhost", 1978)){
    ecode = tcrdbecode(rdb);
    fprintf(stderr, "open error: %s\n", tcrdberrmsg(ecode));
  }

  /* store records */
  if(!tcrdbput2(rdb, "foo", "hop") ||
     !tcrdbput2(rdb, "bar", "step") ||
     !tcrdbput2(rdb, "baz", "jump")){
    ecode = tcrdbecode(rdb);
    fprintf(stderr, "put error: %s\n", tcrdberrmsg(ecode));
  }

  /* retrieve records */
  value = tcrdbget2(rdb, "foo");
  if(value){
    printf("%s\n", value);
    free(value);
  } else {
    ecode = tcrdbecode(rdb);
    fprintf(stderr, "get error: %s\n", tcrdberrmsg(ecode));
  }

  /* close the connection */
  if(!tcrdbclose(rdb)){
    ecode = tcrdbecode(rdb);
    fprintf(stderr, "close error: %s\n", tcrdberrmsg(ecode));
  }

  /* delete the object */
  tcrdbdel(rdb);

  return 0;
}

The following code is an example to use a remote database with the table extension.

#include <tcrdb.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdint.h>

int main(int argc, char **argv){

  TCRDB *rdb;
  int ecode, pksiz, i, rsiz;
  char pkbuf[256];
  const char *rbuf, *name;
  TCMAP *cols;
  RDBQRY *qry;
  TCLIST *res;

  /* create the object */
  rdb = tcrdbnew();

  /* connect to the server */
  if(!tcrdbopen(rdb, "localhost", 1978)){
    ecode = tcrdbecode(rdb);
    fprintf(stderr, "open error: %s\n", tcrdberrmsg(ecode));
  }

  /* store a record */
  pksiz = sprintf(pkbuf, "%ld", (long)tcrdbtblgenuid(rdb));
  cols = tcmapnew3("name", "mikio", "age", "30", "lang", "ja,en,c", NULL);
  if(!tcrdbtblput(rdb, pkbuf, pksiz, cols)){
    ecode = tcrdbecode(rdb);
    fprintf(stderr, "put error: %s\n", tcrdberrmsg(ecode));
  }
  tcmapdel(cols);

  /* store a record in a naive way */
  pksiz = sprintf(pkbuf, "12345");
  cols = tcmapnew();
  tcmapput2(cols, "name", "falcon");
  tcmapput2(cols, "age", "31");
  tcmapput2(cols, "lang", "ja");
  if(!tcrdbtblput(rdb, pkbuf, pksiz, cols)){
    ecode = tcrdbecode(rdb);
    fprintf(stderr, "put error: %s\n", tcrdberrmsg(ecode));
  }
  tcmapdel(cols);

  /* search for records */
  qry = tcrdbqrynew(rdb);
  tcrdbqryaddcond(qry, "age", RDBQCNUMGE, "20");
  tcrdbqryaddcond(qry, "lang", RDBQCSTROR, "ja,en");
  tcrdbqrysetorder(qry, "name", RDBQOSTRASC);
  tcrdbqrysetlimit(qry, 10, 0);
  res = tcrdbqrysearch(qry);
  for(i = 0; i < tclistnum(res); i++){
    rbuf = tclistval(res, i, &rsiz);
    cols = tcrdbtblget(rdb, rbuf, rsiz);
    if(cols){
      printf("%s", rbuf);
      tcmapiterinit(cols);
      while((name = tcmapiternext2(cols)) != NULL){
        printf("\t%s\t%s", name, tcmapget2(cols, name));
      }
      printf("\n");
      tcmapdel(cols);
    }
  }
  tclistdel(res);
  tcrdbqrydel(qry);

  /* close the connection */
  if(!tcrdbclose(rdb)){
    ecode = tcrdbecode(rdb);
    fprintf(stderr, "close error: %s\n", tcrdberrmsg(ecode));
  }

  /* delete the object */
  tcrdbdel(rdb);

  return 0;
}

How to Use the Library

The API of C is available by programs conforming to the C89 (ANSI C) standard or the C99 standard. As the header files of Tokyo Tyrant are provided as `tcrdb.h', applications should include it to use the API. As the library is provided as `libtokyotyrant.a' and `libtokyotyrant.so' and they depend on `libtokyocabinet.so', `libz.so', `libbz2.so', `libresolv.so', `libnsl.so', `libdl.so', `librt.so', `libpthread.so', `libm.so', and `libc.so', linker options corresponding to them are required by the build command. The typical build command is the following.

gcc -I/usr/local/include tt_example.c -o tt_example \
  -L/usr/local/lib -ltokyotyrant -ltokyocabinet -lz -lbz2 \
  -lresolv -lnsl -ldl -lrt -lpthread -lm -lc

You can also use Tokyo Tyrant in programs written in C++. Because each header is wrapped in C linkage (`extern "C"' block), you can simply include them into your C++ programs.


Lua Extension

The database server can starts reading a Lua script file by the `-ext' option. Clients can call functions defined in the script file by the `tcrdbext' function of the remote database API.

User-defined Functions

You can define some arbitrary functions in the script file. Each function receives two string parameters of the key and the value. The return value is sent back to the client. If the function returns `nil', the server send the error code to the client.

Two kinds of locking options are provided to `tcrdbext'. One is global locking which means that only one thread can operate the function at the same time. The other is record locking which means that only one thread can operate the record of the specified key at the same time.

Note that instances of Lua interpreter are handled separately by each native thread. Because global variables of Lua are not useful to share some data among native threads or sessions, shared data should be handled in the database or by the stash functions.

Built-in Functions

The following build-in functions for database operations are available in user defined functions. The type of `key' and `value' parameters should be string or number. If number is given, it is converted as decimal string.

_eval(chunk)
Evaluate a Lua chunk in each native thread.
`chunk' specifies the Lua chunk string.
If successful, the return value is true, else, it is false.
_log(message, level)
Print a message into the server log.
`message' specifies the message string.
`level' specifies the log level; 0 for debug, 1 for information, 2 for error, 3 for system. It can be omitted and the default value is 1.
_put(key, value)
Store a record.
`key' specifies the key.
`value' specifies the value.
If successful, the return value is true, else, it is false.
_putkeep(key, value)
Store a new record.
`key' specifies the key.
`value' specifies the value.
If successful, the return value is true, else, it is false.
_putcat(key, value)
Concatenate a value at the end of the existing record.
`key' specifies the key.
`value' specifies the value.
If successful, the return value is true, else, it is false.
_out(key)
Remove a record.
`key' specifies the key.
If successful, the return value is true, else, it is false.
_get(key)
Retrieve a record.
`key' specifies the key.
If successful, the return value is the value of the corresponding record. `nil' is returned if no record corresponds.
_vsiz(key)
Get the size of the value of a record.
`key' specifies the key.
If successful, the return value is the size of the value of the corresponding record, else, it is -1.
_iterinit()
Initialize the iterator.
If successful, the return value is true, else, it is false.
_iternext()
Get the next key of the iterator.
If successful, the return value is the next key, else, it is `nil'. `nil' is returned when no record is to be get out of the iterator.
_fwmkeys(prefix, max)
Get forward matching keys.
The return value an array of the keys of the corresponding records. This function does never fail. It returns an empty array even if no record corresponds.
_addint(key, value)
Add an integer to a record.
`key' specifies the key.
`value' specifies the value.
If successful, the return value is the summation value, else, it is `nil'.
_adddouble(key, value)
Add a real number to a record.
`key' specifies the key.
`value' specifies the value.
If successful, the return value is the summation value, else, it is `nil'.
_vanish()
Remove all records.
If successful, the return value is true, else, it is false.
_rnum()
Get the number of records.
The return value is the number of records.
_size()
Get the size of the database file.
The return value is the size of the database file.
_misc(name, args, ...)
Call a versatile function for miscellaneous operations.
`name' specifies the name of the function. If it begins with "$", the update log is omitted.
`args' specifies an array of arguments.
If successful, the return value is an array of the result. `nil' is returned on failure.
_foreach(func)
Process each record atomically.
`func' the iterator function called for each record. It receives two parameters of the key and the value, and returns true to continue iteration or false to stop iteration.
If successful, the return value is true, else, it is false.
_mapreduce(mapper, reducer, keys)
Perform operations based on MapReduce.
`mapper' specifies the mapper function. It is called for each target record and receives the key, the value, and the function to emit the mapped records. The emitter function receives a key and a value. The mapper function should return true normally or false on failure.
`reducer' specifies the reducer function. It is called for each record generated by sorting emitted records by keys, and receives the key and an array of values. The reducer function should return true normally or false on failure.
`keys' specifies the keys of target records. If it is not defined, every record in the database is processed.
If successful, the return value is true, else, it is false.
_stashput(key, value)
Store a record into the stash.
`key' specifies the key.
`value' specifies the value.
The return value is always true.
_stashout(key)
Remove a record from the stash.
`key' specifies the key.
If successful, the return value is true, else, it is false.
_stashget(key)
Retrieve a record in the stash.
`key' specifies the key.
If successful, the return value is the value of the corresponding record. `nil' is returned if no record corresponds.
_stashvanish()
Remove all records of the stash.
_stashforeach(func)
Process each record atomically of the stash.
`func' the iterator function called for each record. It receives two parameters of the key and the value, and returns true to continue iteration or false to stop iteration.
If successful, the return value is true, else, it is false.
_lock(key)
Lock an arbitrary key.
`key' specifies the key. The locked key should be unlocked in the same operation.
If successful, the return value is true, else, it is false.
_unlock(key)
Unock an arbitrary key.
`key' specifies the key.
If successful, the return value is true, else, it is false.
_pack(format, ary, ...)
Serialize an array of numbers into a string.
`format' specifies the format string. It should be composed of conversion characters; `c' for int8_t, `C' for uint8_t, `s' for int16_t, `S' for uint16_t, `i' for int32_t, `I' for uint32_t, `l' for int64_t, `L' for uint64_t, `f' for float, `d' for double, `n' for uint16_t in network byte order, `N' for uint32_t in network byte order, `M' for uint64_t in network byte order, and `w' for BER encoding. They can be trailed by a numeric expression standing for the iteration count or by `*' for the rest all iteration.
`ary' specifies the array of numbers.
The return value is the serialized string.
_unpack(format, str)
Deserialize a binary string into an array of numbers.
`format' specifies the format string. It should be composed of conversion characters as with `_pack'.
`str' specifies the binary string.
The return value is the deserialized array.
_split(str, delims)
Split a string into substrings.
`str' specifies the string.
`delims' specifies a string including separator characters. If it is not defined, the zero code is specified.
The return value is an array of substrings.
_codec(mode, str)
Encode or decode a string.
`mode' specifies the encoding method; "url" for URL encoding, "~url" for URL decoding, "base" for Base64 encoding, "~base" for Base64 decoding, "hex" for hexadecimal encoding, "~hex" for hexadecimal decoding, "pack" for PackBits encoding, "~pack" for PackBits decoding, "tcbs" for TCBS encoding, "~tcbs" for TCBS decoding, "deflate" for Deflate encoding, "~deflate" for Deflate decoding, "gzip" for GZIP encoding, "~gzip" for GZIP decoding, "bzip" for BZIP2 encoding, "~bzip" for BZIP2 decoding, "xml" for XML escaping, "~xml" for XML unescaping.
`str' specifies the string.
The return value is the encoded or decoded string.
_hash(mode, str)
Get the hash value of a string.
`mode' specifies the hash method; "md5" for MD5 in hexadecimal format, "md5raw" for MD5 in raw format, "crc32" for CRC32 checksum number.
`str' specifies the string.
The return value is the hash value.
_bit(mode, num, aux)
Perform bit operation of an integer.
`mode' specifies the operator; "and" for bitwise-and operation, "or" for bitwise-or operation, "xor" for bitwise-xor operation, "not" for bitwise-not operation, "left" for left shift operation, "right" for right shift operation.
`num' specifies the integer, which is treated as a 32-bit unsigned integer.
`aux' specifies the auxiliary operand for some operators.
The return value is the result value.
_strstr(str, pattern, alt)
Perform substring matching or replacement without evaluating any meta character.
`str' specifies the source string.
`pattern' specifies the matching pattern.
`alt' specifies the alternative string corresponding for the pattern. If it is not defined, matching check is performed.
If the alternative string is specified, the converted string is returned. If the alternative string is not specified, the index of the substring matching the given pattern or 0 is returned.
_regex(str, pattern, alt)
Perform pattern matching or replacement with regular expressions.
`str' specifies the source string.
`pattern' specifies the pattern of regular expressions.
`alt' specifies the alternative string corresponding for the pattern. If it is not defined, matching check is performed.
If the alternative string is specified, the converted string is returned. If the alternative string is not specified, the boolean value of whether the source string matches the pattern is returned.
_ucs(data)
Convert a UTF-8 string into a UCS-2 array or its inverse.
`data' specifies the target data. If it is a string, convert it into a UCS-array. If it is an array, convert it into a UTF-8 string.
The return value is the result data.
_dist(astr, bstr, isutf)
Calculate the edit distance of two UTF-8 strings.
`astr' specifies a string.
`bstr' specifies the other string.
`isutf' specifies whether to calculate cost by Unicode character. If it is not defined, false is specified and calculate cost by ASCII character.
The return value is the edit distance which is known as the Levenshtein distance.
_isect(ary, ...)
Calculate the intersection set of arrays.
`ary' specifies the arrays. Arbitrary number of arrays can be specified as the parameter list.
The return value is the array of the intersection set.
_union(ary, ...)
Calculate the union set of arrays.
`ary' specifies the arrays. Arbitrary number of arrays can be specified as the parameter list.
The return value is the array of the union set.
_time()
Get the time of day in seconds.
The return value is the time of day in seconds. The accuracy is in microseconds.
_sleep(sec)
Suspend execution for the specified interval.
`sec' specifies the interval in seconds.
If successful, the return value is true, else, it is false.
_stat(path)
Get the status of a file.
`path' specifies the path of the file.
If successful, the return value is a table containing status, else, it is `nil'. There are keys of status name; "dev", "ino", "mode", "nlink", "uid", "gid", "rdev", "size", "blksize", "blocks", "atime", "mtime", "ctime", which have same meanings of the POSIX "stat" call. Additionally, "_regular" for whether the file is a regular file, "_directory" for whether the file is a directory, "_readable" for whether the file is readable by the process, "_writable" for whether the file is writable by the process, "_executable" for whether the file is executable by the process, "_realpath" for the real path of the file, are supported.
_glob(pattern)
Find pathnames matching a pattern.
`pattern' specifies the matching pattern. "?" and "*" are meta characters.
The return value is an array of matched paths. If no path is matched, an empty array is returned.
_remove(path)
Remove a file or a directory and its sub ones recursively.
`path' specifies the path of the link.
If successful, it is true, else, it is false.
_mkdir(path)
Create a directory.
`path' specifies the path of the directory.
If successful, it is true, else, it is false.

Built-in functions, whose names start with "_", cannot be called directly by clients. When the server starts, the function `_begin' is called implicitly if it has been defined. When the server starts, the function `_end' is called implicitly if it has been defined.

The global variable `_version' contains the version information of the server. The global variable `_pid' contains the process ID. The global variable `_sid' contains the server ID. The global variable `_thnum' contains the number of native threads. The global variable `_thid' contains the ID number of each native thread.

Example Code

The following code is an example to increment the record value and store as a decimal number string. This function should be called with the record locking option to ensure atomicity.

function incr(key, value)
   value = tonumber(value)
   if not value then
      return nil
   end
   local old = tonumber(_get(key))
   if old then
      value = value + old
   end
   if not _put(key, value) then
      return nil
   end
   return value
end

Protocol

The protocol between the server and clients stands on TCP/IP. By default, the service port is bound to every address of the local host and the port number is 1978. Each session of the server and a client is composed of a request and a response. The server speaks three protocols at the same port.

Original Binary Protocol

In the original binary protocol, requests are classified into the following commands. Structure of request and response is determined by the command. The byte order of integer in request and response is big endian.

put: for the function `tcrdbput'
Request: [magic:2][ksiz:4][vsiz:4][kbuf:*][vbuf:*]
Two bytes of the command ID: 0xC8 and 0x10
A 32-bit integer standing for the length of the key
A 32-bit integer standing for the length of the value
Arbitrary data of the key
Arbitrary data of the value
Response: [code:1]
An 8-bit integer whose value is 0 on success or another on failure
putkeep: for the function `tcrdbputkeep'
Request: [magic:2][ksiz:4][vsiz:4][kbuf:*][vbuf:*]
Two bytes of the command ID: 0xC8 and 0x11
A 32-bit integer standing for the length of the key
A 32-bit integer standing for the length of the value
Arbitrary data of the key
Arbitrary data of the value
Response: [code:1]
An 8-bit integer whose value is 0 on success or another on failure
putcat: for the function `tcrdbputcat'
Request: [magic:2][ksiz:4][vsiz:4][kbuf:*][vbuf:*]
Two bytes of the command ID: 0xC8 and 0x12
A 32-bit integer standing for the length of the key
A 32-bit integer standing for the length of the value
Arbitrary data of the key
Arbitrary data of the value
Response: [code:1]
An 8-bit integer whose value is 0 on success or another on failure
putshl: for the function `tcrdbputshl'
Request: [magic:2][ksiz:4][vsiz:4][width:4][kbuf:*][vbuf:*]
Two bytes of the command ID: 0xC8 and 0x13
A 32-bit integer standing for the length of the key
A 32-bit integer standing for the length of the value
A 32-bit integer standing for the width
Arbitrary data of the key
Arbitrary data of the value
Response: [code:1]
An 8-bit integer whose value is 0 on success or another on failure
putnr: for the function `tcrdbputnr'
Request: [magic:2][ksiz:4][vsiz:4][kbuf:*][vbuf:*]
Two bytes of the command ID: 0xC8 and 0x18
A 32-bit integer standing for the length of the key
A 32-bit integer standing for the length of the value
Arbitrary data of the key
Arbitrary data of the value
Response: (none)
out: for the function `tcrdbout'
Request: [magic:2][ksiz:4][kbuf:*]
Two bytes of the command ID: 0xC8 and 0x20
A 32-bit integer standing for the length of the key
Arbitrary data of the key
Response: [code:1]
An 8-bit integer whose value is 0 on success or another on failure
get: for the function `tcrdbget'
Request: [magic:2][ksiz:4][kbuf:*]
Two bytes of the command ID: 0xC8 and 0x30
A 32-bit integer standing for the length of the key
Arbitrary data of the key
Response: [code:1]([vsiz:4][vbuf:*])
An 8-bit integer whose value is 0 on success or another on failure
on success: A 32-bit integer standing for the length of the value
on success: Arbitrary data of the value
mget: for the function `tcrdbget3'
Request: [magic:2][rnum:4][{[ksiz:4][kbuf:*]}:*]
Two bytes of the command ID: 0xC8 and 0x31
A 32-bit integer standing for the number of keys
iteration: A 32-bit integer standing for the length of the key
iteration: Arbitrary data of the key
Response: [code:1][rnum:4][{[ksiz:4][vsiz:4][kbuf:*][vbuf:*]}:*]
An 8-bit integer whose value is 0 on success or another on failure
A 32-bit integer standing for the number of records
iteration: A 32-bit integer standing for the length of the key
iteration: A 32-bit integer standing for the length of the value
iteration: Arbitrary data of the key
iteration: Arbitrary data of the value
vsiz: for the function `tcrdbvsiz'
Request: [magic:2][ksiz:4][kbuf:*]
Two bytes of the command ID: 0xC8 and 0x38
A 32-bit integer standing for the length of the key
Arbitrary data of the key
Response: [code:1]([vsiz:4])
An 8-bit integer whose value is 0 on success or another on failure
on success: A 32-bit integer standing for the length of the value
iterinit: for the function `tcrdbiterinit'
Request: [magic:2]
Two bytes of the command ID: 0xC8 and 0x50
Response: [code:1]
An 8-bit integer whose value is 0 on success or another on failure
iternext: for the function `tcrdbiternext'
Request: [magic:2]
Two bytes of the command ID: 0xC8 and 0x51
Response: [code:1]([ksiz:4][kbuf:*])
An 8-bit integer whose value is 0 on success or another on failure
on success: A 32-bit integer standing for the length of the key
on success: Arbitrary data of the key
fwmkeys: for the function `tcrdbfwmkeys'
Request: [magic:2][psiz:4][max:4][pbuf:*]
Two bytes of the command ID: 0xC8 and 0x58
A 32-bit integer standing for the length of the prefix
A 32-bit integer standing for the maximum number of keys to be fetched
Arbitrary data of the prefix
Response: [code:1][knum:4][{[ksiz:4][kbuf:*]}:*]
An 8-bit integer whose value is 0 on success or another on failure
A 32-bit integer standing for the number of keys
iteration: A 32-bit integer standing for the length of the key
iteration: Arbitrary data of the key
addint: for the function `tcrdbaddint'
Request: [magic:2][ksiz:4][num:4][kbuf:*]
Two bytes of the command ID: 0xC8 and 0x60
A 32-bit integer standing for the length of the key
A 32-bit integer standing for the additional number
Arbitrary data of the key
Response: [code:1]([sum:4])
An 8-bit integer whose value is 0 on success or another on failure
on success: A 32-bit integer standing for the summation value
adddouble: for the function `tcrdbadddouble'
Request: [magic:2][ksiz:4][integ:8][fract:8][kbuf:*]
Two bytes of the command ID: 0xC8 and 0x61
A 32-bit integer standing for the length of the key
A 64-bit integer standing for the integral of the additional number
A 64-bit integer standing for the trillionfold fractional of the additional number
Arbitrary data of the key
Response: [code:1]([integ:8][fract:8])
An 8-bit integer whose value is 0 on success or another on failure
on success: A 64-bit integer standing for the integral of the summation value
on success: A 64-bit integer standing for the trillionfold fractional of the summation value
ext: for the function `tcrdbext'
Request: [magic:2][nsiz:4][opts:4][ksiz:4][vsiz:4][nbuf:*][kbuf:*][vbuf:*]
Two bytes of the command ID: 0xC8 and 0x68
A 32-bit integer standing for the length of the function name
A 32-bit integer standing for the options
A 32-bit integer standing for the length of the key
A 32-bit integer standing for the length of the value
Arbitrary data of the function name
Arbitrary data of the key
Arbitrary data of the value
Response: [code:1]([rsiz:4][rbuf:*])
An 8-bit integer whose value is 0 on success or another on failure
on success: A 32-bit integer standing for the length of the result
on success: Arbitrary data of the result
sync: for the function `tcrdbsync'
Request: [magic:2]
Two bytes of the command ID: 0xC8 and 0x70
Response: [code:1]
An 8-bit integer whose value is 0 on success or another on failure
optimize: for the function `tcrdboptimize'
Request: [magic:2][psiz:4][pbuf:*]
Two bytes of the command ID: 0xC8 and 0x71
A 32-bit integer standing for the length of the parameter string
Arbitrary data of the parameter string
Response: [code:1]
An 8-bit integer whose value is 0 on success or another on failure
vanish: for the function `tcrdbvanish'
Request: [magic:2]
Two bytes of the command ID: 0xC8 and 0x72
Response: [code:1]
An 8-bit integer whose value is 0 on success or another on failure
copy: for the function `tcrdbcopy'
Request: [magic:2][psiz:4][pbuf:*]
Two bytes of the command ID: 0xC8 and 0x73
A 32-bit integer standing for the length of the path
Arbitrary data of the path
Response: [code:1]
An 8-bit integer whose value is 0 on success or another on failure
restore: for the function `tcrdbrestore'
Request: [magic:2][psiz:4][ts:8][opts:4][pbuf:*]
Two bytes of the command ID: 0xC8 and 0x74
A 32-bit integer standing for the length of the path
A 64-bit integer standing for the beginning time stamp in microseconds
A 32-bit integer standing for the options
Arbitrary data of the path
Response: [code:1]
An 8-bit integer whose value is 0 on success or another on failure
setmst: for the function `tcrdbsetmst'
Request: [magic:2][hsiz:4][port:4][ts:8][opts:4][host:*]
Two bytes of the command ID: 0xC8 and 0x78
A 32-bit integer standing for the length of the host name
A 32-bit integer standing for the port number
A 64-bit integer standing for the beginning time stamp in microseconds
A 32-bit integer standing for the options
Arbitrary data of the host name
Response: [code:1]
An 8-bit integer whose value is 0 on success or another on failure
rnum: for the function `tcrdbrnum'
Request: [magic:2]
Two bytes of the command ID: 0xC8 and 0x80
Response: [code:1][rnum:8]
An 8-bit integer whose value is always 0
A 64-bit integer standing for the number of records
size: for the function `tcrdbsize'
Request: [magic:2]
Two bytes of the command ID: 0xC8 and 0x81
Response: [code:1][rnum:8]
An 8-bit integer whose value is always 0
A 64-bit integer standing for the size of the database
stat: for the function `tcrdbstat'
Request: [magic:2]
Two bytes of the command ID: 0xC8 and 0x88
Response: [code:1][ssiz:4][sbuf:*]
An 8-bit integer whose value is always 0
A 32-bit integer standing for the length of the status message
Arbitrary data of the result
misc: for the function `tcrdbmisc'
Request: [magic:2][nsiz:4][opts:4][rnum:4][nbuf:*][{[asiz:4][abuf:*]}:*]
Two bytes of the command ID: 0xC8 and 0x90
A 32-bit integer standing for the length of the function name
A 32-bit integer standing for the options
A 32-bit integer standing for the number of arguments
Arbitrary data of the function name
iteration: A 32-bit integer standing for the length of the argument
iteration: Arbitrary data of the argument
Response: [code:1][rnum:4][{[esiz:4][ebuf:*]}:*]
An 8-bit integer whose value is 0 on success or another on failure
A 32-bit integer standing for the number of result elements
iteration: A 32-bit integer standing for the length of the element
iteration: Arbitrary data of the element

To finish the session, the client can shutdown and close the socket at any time. If not closed, the connection can be reused for the next session. If protocol violation or some fatal error occurs, the server immediately breaks the session and closes the connection.

Memcached Compatible Protocol

As for the memcached (ASCII) compatible protocol, the server implements the following commands; "set", "add", "replace", "get", "delete", "incr", "decr", "stats", "flush_all", "version", and "quit". "noreply" options of update commands are also supported. However, "flags", "exptime", and "cas unique" parameters are ignored.

HTTP Compatible Protocol

As for the HTTP (1.1) compatible protocol, the server implements the following commands; "GET" (relevant to `tcrdbget'), "HEAD" (relevant to `tcrdbvsiz'), "PUT" (relevant to `tcrdbput'), "POST" (relevant to `tcrdbext'), "DELETE" (relevant to `tcrdbout'), and "OPTIONS" (relevant to `tcrdbstat'). The URI of each request is treated as the key encoded by the URL encoding. And the entity body is treated as the value. However, headers except for "Connection" and "Content-Length" are ignored. "PUT" can have the header "X-TT-PDMODE" whose value is either of 1 (relevant to `tcrdbputkeep'), 2 (relevant to `tcrdbputcat'), or else (relevant to `tcrdbput').

"POST" should have one of the header "X-TT-XNAME" or the header "X-TT-MNAME". "X-TT-XNAME" is relevant to `tcrdbext' and specifies the function name. The header "X-TT-XOPTS" stands for bitwise-or options of 1 (record locking) and 2 (global locking). The URI of each request is treated as the key encoded by the URL encoding. And the entity body is treated as the value. The result is expressed as the entity body of the response. "X-TT-MNAME" is relevant to `tcrdbmisc' and specifies the function name. The header "X-TT-MOPTS" stands for bitwise-or options of 1 (omission of the update log). The request parameters are expressed as the entity body in the "application/x-www-form-urlencoded" format. The names are ignored and the values are treated as a list of the parameters. The result is expressed as the entity body of the response in the "application/x-www-form-urlencoded" format.


Tutorial

Basic Use

After installation of Tokyo Tyrant, you can start the server immediately by executing the command `ttserver' in the terminal. By default, the server listens to the port 1978 and serves as the accessor of an on-memory hash database, which is useful to store cache data.

[terminal-1]$ ttserver

To test storing operations, execute the following commands in another terminal. `tcrmgr put' calls the function `tcrdbput'.

[terminal-2]$ tcrmgr put localhost one first
[terminal-2]$ tcrmgr put localhost two second
[terminal-2]$ tcrmgr put localhost three third

To test retrieving operations, execute the following commands in another terminal. `tcrmgr get' calls the function `tcrdbget'.

[terminal-2]$ tcrmgr get localhost one
[terminal-2]$ tcrmgr get localhost two
[terminal-2]$ tcrmgr get localhost three

To retrieve multiple records at once, execute the following command. `tcrmgr mget' calls the function `tcrdbget3'.

[terminal-2]$ tcrmgr mget localhost one two three

To terminate the server, input Ctrl-C in the terminal of the server.

Next, let's run the server that handles a hash database, by specifying the file name whose suffix is `.tch'.

[terminal-1]$ ttserver casket.tch

Store some records.

[terminal-2]$ tcrmgr put localhost one first
[terminal-2]$ tcrmgr put localhost two second
[terminal-2]$ tcrmgr put localhost three third

Terminate the server by Ctrl-C, and then restart the server.

[terminal-1]$ ttserver casket.tch

Check consistency of stored records.

[terminal-2]$ tcrmgr mget localhost one two three

Terminate the server by Ctrl-C and remove the database, for the successive tutorial.

[terminal-1]$ rm casket.tch

Daemon

To run the server as a daemon process, specify the option `-dmn'. Moreover, the option `-pid' should be specified to record the process ID into a file. Note that the current directory of the daemon process is changed to the root directory. So, the file path parameter should be expressed as the absolute path.

[terminal-1]$ ttserver -dmn -pid /tmp/ttserver.pid /tmp/casket.tch

To terminate the daemonized server, check the process ID from the file specified by `-pid' and send the SIGTERM signal to the process.

[terminal-1]$ kill -TERM `cat /tmp/ttserver.pid`

To run the server by the RC script of the operating system, use `ttservctl'. As for most Linux distribution, append the following line to `/etc/rc.local'.

/usr/local/sbin/ttservctl start

By default, the database file and the related files are placed under `/var/ttserver'. Because `ttservctl' is a tiny shell script, copy and edit it for your purpose. Also, it is suitable to install the modified script into `/etc/init.d' and set symbolic links from `/etc/rc3.d/S98ttserver' and `/etc/rc5.d/S98ttserver'.

Backup and Recovery

Let's run the server again to continue this tutorial.

[terminal-1]$ ttserver casket.tch

Store some records.

[terminal-2]$ tcrmgr put localhost one first
[terminal-2]$ tcrmgr put localhost two second
[terminal-2]$ tcrmgr put localhost three third

To back up the database file, indicate the destination path to the server by the command `tcrmgr copy'. Note that the backup file is created on the local file system of the server (not on the client side).

[terminal-2]$ tcrmgr copy localhost backup.tch

Terminate the server by Ctrl-C and remove the database.

[terminal-1]$ rm casket.tch

Recover the database from the backup file and restart the server.

[terminal-1]$ cp backup.tch casket.tch
[terminal-1]$ ttserver casket.tch

Check consistency of stored records.

[terminal-2]$ tcrmgr mget localhost one two three

Terminate the server by Ctrl-C and remove the databases, for the successive tutorial.

[terminal-1]$ rm casket.tch backup.tch

Update Log

Let's run the server with update logging enabled. The option `-ulog' specifies the directory to contain the update log files.

[terminal-1]$ mkdir ulog
[terminal-1]$ ttserver -ulog ulog casket.tch

Store some records.

[terminal-2]$ tcrmgr put localhost one first
[terminal-2]$ tcrmgr put localhost two second
[terminal-2]$ tcrmgr put localhost three third

Terminate the server by Ctrl-C and remove the database.

[terminal-1]$ rm casket.tch

Escape the update log directoty and restart the server.

[terminal-1]$ mv ulog ulog-back
[terminal-1]$ mkdir ulog
[terminal-1]$ ttserver -ulog ulog casket.tch

Restore the database from the escaped update log, by the command `tcrmgr restore' on the client side.

[terminal-2]$ tcrmgr restore localhost ulog-back

Check consistency of stored records.

[terminal-2]$ tcrmgr mget localhost one two three

Terminate the server by Ctrl-C and remove the database, for the successive tutorial.

[terminal-1]$ rm -rf casket.tch ulog ulog-back

Replication

Replication is a mechanism to synchronize two or more database servers for high availability and high integrity. The replication source server is called "master" and each destination server is called "slave". Replication requires the following preconditions.

  • The master must record the update log.
  • The master must specify the unique server ID.
  • Each slave must record the update log because it may become the master when fail over.
  • Each slave must specify the unique server ID because it may become the master when fail over.
  • Each slave must specify the address and the port number of the master server.
  • Each slave must specify the replication time stamp file.

This section describes how to set up one master (at port 1978) and one slave (at port 1979) replication. First, let's run the master server.

[terminal-1]$ mkdir ulog-1
[terminal-1]$ ttserver -port 1978 -ulog ulog-1 -sid 1 casket-1.tch

Next, let's run the slave server in another terminal.

[terminal-2]$ mkdir ulog-2
[terminal-2]$ ttserver -port 1979 -ulog ulog-2 -sid 2 \
                -mhost localhost -mport 1978 -rts 2.rts casket-2.tch

Store some records into the master.

[terminal-3]$ tcrmgr put -port 1978 localhost one first
[terminal-3]$ tcrmgr put -port 1978 localhost two second
[terminal-3]$ tcrmgr put -port 1978 localhost three third

Check consistency of stored records in the master and the slave.

[terminal-2]$ tcrmgr mget -port 1978 localhost one two three
[terminal-2]$ tcrmgr mget -port 1979 localhost one two three

Let's simulate the case that the master is crashed. Terminate the master by Ctrl-C and remove the database file.

[terminal-1]$ rm casket-1.tch

Terminate the slave by Ctrl-C and restart it as the new master.

[terminal-2]$ ttserver -port 1979 -ulog ulog-2 -sid 2 casket-2.tch

Add the new slave (at port 1980).

[terminal-1]$ mkdir ulog-3
[terminal-1]$ ttserver -port 1980 -ulog ulog-3 -sid 3 \
                -mhost localhost -mport 1979 -rts 3.rts casket-3.tch

Check consistency of stored records in the new master and the new slave.

[terminal-2]$ tcrmgr mget -port 1979 localhost one two three
[terminal-2]$ tcrmgr mget -port 1980 localhost one two three

Terminate the two servers by Ctrl-C and remove the database and related files.

[terminal-1]$ rm -rf casket-1.tch ulog-1 1.rts
[terminal-2]$ rm -rf casket-2.tch ulog-2 2.rts
[terminal-1]$ rm -rf casket-3.tch ulog-3 3.rts

Tokyo Tyrant supports "dual master" replication which realizes higher availability. To do it, run two servers which replicate each other. Note that updating both of the masters at the same time may cause inconsistency of their databases. By default, the servers do not complain even if inconsistency is detected. The option `-rcc' make them check the consistency and stop replication when inconsistency is detected.

Setting Replication on Demand

You can set replication of the running database service without any downtime. First, prepare the following script for backup operation and save it as "ttbackup.sh" with executable permission (0755).

#! /bin/sh
srcpath="$1"
destpath="$1.$2"
rm -f "$destpath"
cp -f "$srcpath" "$destpath"

Next, let's run the master with update log enabled.

[terminal-1]$ mkdir ulog-1
[terminal-1]$ ttserver -port 1978 -ulog ulog-1 -sid 1 casket-1.tch

Store a volume of records into the master.

[terminal-2]$ tcrtest write -port 1978 localhost 10000

Check consistency of stored records.

[terminal-2]$ tcrmgr list -port 1978 -pv localhost

Backup the database.

[terminal-2]$ tcrmgr copy -port 1978 localhost '@./ttbackup.sh'

Confirm that the backup file was saved as "casket-1.tch.xxxxx" ("xxxxx" stands for the time stamp of the backup file). Then, run the slave with the backup file.

[terminal-2]$ ls
[terminal-2]$ cp casket-1.tch.xxxxx casket-2.tch
[terminal-2]$ echo xxxxx > 2.rts
[terminal-2]$ mkdir ulog-2
[terminal-2]$ ttserver -port 1979 -ulog ulog-2 -sid 2 -rts 2.rts casket-2.tch

Note that the above operation did not specify the master server to the slave. For tutorial, let's simulate that some records are stored into the master by users while you are setting replication.

[terminal-3]$ tcrmgr put -port 1978 localhost one first
[terminal-3]$ tcrmgr put -port 1978 localhost two second
[terminal-3]$ tcrmgr put -port 1978 localhost three third

Check the difference between the master and the slave.

[terminal-3]$ tcrmgr inform -port 1978 localhost
[terminal-3]$ tcrmgr inform -port 1979 localhost

Specify the master to the slave so that replication will start and the difference will be resolved.

[terminal-3]$ tcrmgr setmst -port 1979 -mport 1978 localhost localhost

Confirm that the slave knows the master and the difference has been resolved.

[terminal-3]$ tcrmgr inform -port 1979 -st localhost

Terminate the two servers by Ctrl-C and remove the database and related files.

[terminal-1]$ rm -rf casket-1.tch casket-1.tch.* ulog-1 1.rts ttbackup.sh
[terminal-2]$ rm -rf casket-2.tch ulog-2 2.rts

Tuning

If you use a hash database, set the tuning parameter "#bnum=xxx" to improve performance. It specifies the bucket number and should be more than the number of record to be stored.

If you use a B+ tree database, set the tuning parameters "#lcnum=xxx#bnum=yyy" to improve performance. The former specifies the maximum number of leaf nodes to be cached. It should be larger as long as the capacity of RAM on the system allows. The latter specifies the bucket number and should be more than 1/128 of the number of records to be stored.

If huge number of clients access the server, make sure the limit number of file descriptors per process is cleared. By default on most systems, it is set as 1024. If so, use `ulimit' to clear it.

In order to deal with rushing queries at the peak time of your service, replication combining the on-memory hash/tree database and the file hash/tree database is useful. The master server handles the on-memory database and it can come through rushing queries at the peak time. Though the on-memory database can not assure the data persistence, the slave of replication compensates the shortage by storing records in the file database.

Lua Extension

If you want more complex database operations than existing ones, use the Lua extension. For example, prepare the following script and save it as "test.lua". There is a function "fibonacci" which returns the Fibonacci number of a number of the key.

function fibonacci(key, value)
   local num = tonumber(key)
   local large = math.pow((1 + math.sqrt(5)) / 2, num)
   local small = math.pow((1 - math.sqrt(5)) / 2, num)
   return (large - small) / math.sqrt(5)
end

Let's start the server by making it read the script file.

[terminal-1]$ ttserver -ext test.lua

Call the function from the client command.

[terminal-2]$ tcrmgr ext localhost fibonacci 1
[terminal-2]$ tcrmgr ext localhost fibonacci 2
[terminal-2]$ tcrmgr ext localhost fibonacci 3
[terminal-2]$ tcrmgr ext localhost fibonacci 4
[terminal-2]$ tcrmgr ext localhost fibonacci 5
[terminal-2]$ tcrmgr ext localhost fibonacci 6

Fibonacci numbers can be generated by another algorithm, which is naive and stateful. Add the following script to "test.lua". There is a function "fibnext" which returns the next Fibonacci number from the database. The state information are stored in the database.

function fibnext(key, value)
   local cur = tonumber(_get("fibcur"))
   if not cur then
      _put("fibold", 0)
      _put("fibcur", 1)
      return 1
   end
   local old = tonumber(_get("fibold"))
   _put("fibold", cur)
   cur = old + cur
   _put("fibcur", cur)
   return cur
end

Then, restart the server and test the new algorithm.

[terminal-2]$ tcrmgr ext localhost fibnext
[terminal-2]$ tcrmgr ext localhost fibnext
[terminal-2]$ tcrmgr ext localhost fibnext
[terminal-2]$ tcrmgr ext localhost fibnext
[terminal-2]$ tcrmgr ext localhost fibnext
[terminal-2]$ tcrmgr ext localhost fibnext

As you see, the called function receives two string parameters of the key and the value. The return value is sent back to the client. You can use such built-in functions for database operations as "_put", "_out", "_get", and so on. There is a sample file `ext/senatus.lua'.

Using memcached Client

This section describes how to use a memcached client library of Perl (Cache::Memcached) with Tokyo Tyrant. Run the server of Tokyo Tyrant as usual. And, the following script is a typical example.

use Cache::Memcached;

my $memd = Cache::Memcached->new();
$memd->set_servers(['localhost:1978']);

$memd->set('one', 'first');
$memd->set('two', 'second');
$memd->set('three', 'third');

my $val = $memd->get('one');
printf("one: %s\n", $val);

$val = $memd->get_multi('one', 'two', 'three');
printf("one: %s\n", $val->{one});
printf("two: %s\n", $val->{two});
printf("three: %s\n", $val->{three});

$memd->delete('one');

Using HTTP Client

This section describes how to use an HTTP client library of Perl (LWP::UserAgent) with Tokyo Tyrant. Run the server of Tokyo Tyrant as usual. And, the following script is a typical example.

use LWP::UserAgent;

my $ua = LWP::UserAgent->new(keep_alive => 1);
my $baseurl = 'http://localhost:1978/';

my $req;
$req = HTTP::Request->new(PUT => $baseurl . 'one', [], 'first');
$ua->request($req);
$req = HTTP::Request->new(PUT => $baseurl . 'two', ["X-TT-PDMODE" => 1], 'second');
$ua->request($req);
$req = HTTP::Request->new(PUT => $baseurl . 'three', ["X-TT-PDMODE" => 2], 'third');
$ua->request($req);

$req = HTTP::Request->new(GET => $baseurl . 'one');
my $res = $ua->request($req);
if($res->is_success()){
    printf("%s\n", $res->content());
}

$req = HTTP::Request->new(DELETE => $baseurl . 'one');
$res = $ua->request($req);

$req = HTTP::Request->new(POST => $baseurl . 'foo',
  ["X-TT-XNAME" => "echo", "X-TT-XOPTS" => 1], 'bar');
$res = $ua->request($req);
if($res->is_success()){
    printf("%s\n", $res->content());
}

Persistent but Expirable Cache

If you want to cache data like session information for your Web application but want to avoid data loss because of the server crash, using Tokyo Tyrant can be the solution, that is to say, "persistent" but expirable cache. It requires the following preconditions.

  • The server must open a table database.
  • Clients should store each record with an expiration date column.
  • The database should have an index for the expiration date column.
  • The database should enable auto defragmentation.
  • The server must periodically call the user defined function provided through the Lua extension.

First, prepare the following script for expiration and save it as "ttexpire.lua". It will expire records where the value of the "x" column exceeds the current date.

function expire()
   local args = {}
   local cdate = string.format("%d", _time())
   table.insert(args, "addcond\0x\0NUMLE\0" .. cdate)
   table.insert(args, "out")
   local res = _misc("search", args)
   if not res then
      _log("expiration was failed", 2)
   end
end

Start the server by opening a table database which has the index for the "x" column, and by scheduling it to call the expiration function per second.

[terminal-1]$ ttserver -ext ttexpire.lua -extpc expire 1.0 "casket.tct#idx=x:dec#dfunit=8"

Store test records from another terminal.

[terminal-2]$ now=`date +%s`
for((i=1;i<=60;i++)); do
  tcrmgr put -sep '|' localhost "$i" "x|$((now+i))"
done

You can confirm that the records are being removed by expiration.

[terminal-2]$ tcrmgr list -pv -sep '|' localhost

License

Tokyo Tyrant is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License or any later version.

Tokyo Tyrant 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 Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along with Tokyo Tyrant (See the file `COPYING'); if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.

Tokyo Tyrant was written by Mikio Hirabayashi. You can contact the author by e-mail to `hirarin@gmail.com'.


tokyotyrant-1.1.40/example/000077500000000000000000000000001133351147200156375ustar00rootroot00000000000000tokyotyrant-1.1.40/example/Makefile000066400000000000000000000024541133351147200173040ustar00rootroot00000000000000# Makefile for sample programs of Hyper Estraier #================================================================ # Setting Variables #================================================================ # Generic settings SHELL = /bin/sh # Targets MYBINS = tcrdbex tcrdbtblex # Building binaries CC = gcc CFLAGS = -ansi -Wall -pedantic -I. -I.. -Wall LDFLAGS = LIBS = -L. -L.. -ltokyotyrant -ltokyocabinet -lz -lresolv -lnsl -lpthread -lm -lc LDENV = LD_RUN_PATH=/lib:/usr/lib:$(HOME)/lib:/usr/local/lib:.:.. #================================================================ # Suffix rules #================================================================ .SUFFIXES : .SUFFIXES : .c .o .c.o : $(CC) -c $(CFLAGS) $< #================================================================ # Actions #================================================================ all : $(MYBINS) clean : rm -rf $(MYBINS) *.exe *.o a.out check.out gmon.out leak.log casket* *~ .PHONY : all clean #================================================================ # Building binaries #================================================================ tcrdbex : tcrdbex.o $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) $(LIBS) tcrdbtblex : tcrdbtblex.o $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) $(LIBS) # END OF FILE tokyotyrant-1.1.40/example/httptest.pl000066400000000000000000000045011133351147200200530ustar00rootroot00000000000000#! /usr/bin/perl use strict; use warnings; use LWP::UserAgent; my $err = 0; my $ua = LWP::UserAgent->new(keep_alive => 1); my $baseurl = 'http://localhost:1978/'; my $req = HTTP::Request->new(PUT => $baseurl . "%E5%B9%B3%E6%9E%97", [], "mikio"); my $res = $ua->request($req); my $code = $res->code(); if($code != 201){ printf("Error: %s\n", $res->status_line()); $err = 1; } foreach my $i (1..100){ $req = HTTP::Request->new(PUT => $baseurl . $i, [], "mikio:$i"); $res = $ua->request($req); if($res->code() != 201){ printf("Error: %s\n", $res->status_line()); $err = 1; } } foreach my $i (1..10){ my $pdmode = int(rand(3)); $req = HTTP::Request->new(PUT => $baseurl . $i, ["X-TT-PDMODE" => $pdmode], "mikio:$i"); $res = $ua->request($req); $code = $res->code(); if($code != 201 && $code != 409){ printf("Error: %s\n", $res->status_line()); $err = 1; } } for(my $i = 1; $i < 107; $i += 7){ $req = HTTP::Request->new(DELETE => $baseurl . $i); $res = $ua->request($req); if($res->is_error() && $res->code() != 404){ printf("Error: %s\n", $res->status_line()); $err = 1; } } for(my $i = 1; $i < 100; $i += 3){ $req = HTTP::Request->new(POST => $baseurl . $i, ["X-TT-XNAME" => "echo", "X-TT-XOPTS" => 1], "hirabayashi:$i"); $res = $ua->request($req); } for(my $i = 1; $i < 100; $i += 3){ $req = HTTP::Request->new(POST => $baseurl . $i, ["X-TT-MNAME" => "getlist", "X-TT-MOPTS" => 1], "1=$i&2=$i"); $res = $ua->request($req); } foreach my $i (1..107){ $req = HTTP::Request->new(GET => $baseurl . $i); $res = $ua->request($req); $code = $res->code(); if($res->is_success()){ printf("%d: %s\n", $i, $res->content()); } elsif($res->code() != 404){ printf("Error: %s\n", $res->status_line()); $err = 1; } $req = HTTP::Request->new(HEAD => $baseurl . $i); $res = $ua->request($req); if($res->is_success()){ printf("%d: %s\n", $i, $res->header("content-length")); } elsif($res->code() != 404){ printf("Error: %s\n", $res->status_line()); $err = 1; } } if($err){ printf("finished with error\n"); exit(1); } printf("finished successfully\n"); exit(1); tokyotyrant-1.1.40/example/mcftest.pl000066400000000000000000000016121133351147200176410ustar00rootroot00000000000000#! /usr/bin/perl use strict; use warnings; use Cache::Memcached::Fast; my $memd = Cache::Memcached::Fast->new({ servers => [{ address => "localhost:1978", noreply => 1 }], connect_timeout => 10, }); $memd->flush_all(); foreach my $i (1..100){ my $rnd = int(rand(10)); my $key = int(rand(100)); if($rnd == 0){ $memd->add($key, "[add:$i]"); } elsif($rnd == 1){ $memd->replace($key, "[replace:$i]"); } elsif($rnd == 2){ $memd->append($key, "[append:$i]"); } elsif($rnd == 3){ $memd->prepend($key, "[prepend:$i]"); } elsif($rnd == 4){ $memd->delete($key); } elsif($rnd == 5){ $memd->incr($key, 1); } elsif($rnd == 6){ $memd->decr($key, 1); } else { $memd->set($key, "[set:$i]"); } } foreach my $i (1..100){ my $val = $memd->get($i, $i); printf("%d: %s\n", $i, defined($val) ? $val : "(undef)"); } tokyotyrant-1.1.40/example/mctest.pl000066400000000000000000000016131133351147200174740ustar00rootroot00000000000000#! /usr/bin/perl use strict; use warnings; use Cache::Memcached; my $memd = Cache::Memcached->new(); $memd->set_servers(["localhost:1978"]); $memd->flush_all(); foreach my $i (1..100){ $memd->set($i, "mikio:$i"); } foreach my $i (1..101){ $memd->add($i, "hirabayashi:$i"); } foreach my $i (1..100){ $memd->replace($i, $i); } foreach my $i (1..100){ $memd->incr($i, 100); } foreach my $i (1..100){ $memd->decr($i, 10); } for(my $i = 1; $i < 100; $i += 7){ $memd->delete($i); } foreach my $i (1..102){ my $val = $memd->get($i, $i); printf("%d: %s\n", $i, defined($val) ? $val : "(undef)"); } my $stats = $memd->stats(); while(my ($key, $value) = each(%$stats)){ if(ref($value) eq 'HASH'){ while(my ($tkey, $tvalue) = each(%$value)){ printf("%s:%s:%s\n", $key, $tkey, $tvalue); } } else { printf("%s:%s\n", $key, $value); } } tokyotyrant-1.1.40/example/tcrdbex.c000066400000000000000000000020061133351147200174340ustar00rootroot00000000000000#include #include #include #include int main(int argc, char **argv){ TCRDB *rdb; int ecode; char *value; /* create the object */ rdb = tcrdbnew(); /* connect to the server */ if(!tcrdbopen(rdb, "localhost", 1978)){ ecode = tcrdbecode(rdb); fprintf(stderr, "open error: %s\n", tcrdberrmsg(ecode)); } /* store records */ if(!tcrdbput2(rdb, "foo", "hop") || !tcrdbput2(rdb, "bar", "step") || !tcrdbput2(rdb, "baz", "jump")){ ecode = tcrdbecode(rdb); fprintf(stderr, "put error: %s\n", tcrdberrmsg(ecode)); } /* retrieve records */ value = tcrdbget2(rdb, "foo"); if(value){ printf("%s\n", value); free(value); } else { ecode = tcrdbecode(rdb); fprintf(stderr, "get error: %s\n", tcrdberrmsg(ecode)); } /* close the connection */ if(!tcrdbclose(rdb)){ ecode = tcrdbecode(rdb); fprintf(stderr, "close error: %s\n", tcrdberrmsg(ecode)); } /* delete the object */ tcrdbdel(rdb); return 0; } tokyotyrant-1.1.40/example/tcrdbtblex.c000066400000000000000000000037111133351147200201420ustar00rootroot00000000000000#include #include #include #include int main(int argc, char **argv){ TCRDB *rdb; int ecode, pksiz, i, rsiz; char pkbuf[256]; const char *rbuf, *name; TCMAP *cols; RDBQRY *qry; TCLIST *res; /* create the object */ rdb = tcrdbnew(); /* connect to the server */ if(!tcrdbopen(rdb, "localhost", 1978)){ ecode = tcrdbecode(rdb); fprintf(stderr, "open error: %s\n", tcrdberrmsg(ecode)); } /* store a record */ pksiz = sprintf(pkbuf, "%ld", (long)tcrdbtblgenuid(rdb)); cols = tcmapnew3("name", "mikio", "age", "30", "lang", "ja,en,c", NULL); if(!tcrdbtblput(rdb, pkbuf, pksiz, cols)){ ecode = tcrdbecode(rdb); fprintf(stderr, "put error: %s\n", tcrdberrmsg(ecode)); } tcmapdel(cols); /* store a record in a naive way */ pksiz = sprintf(pkbuf, "12345"); cols = tcmapnew(); tcmapput2(cols, "name", "falcon"); tcmapput2(cols, "age", "31"); tcmapput2(cols, "lang", "ja"); if(!tcrdbtblput(rdb, pkbuf, pksiz, cols)){ ecode = tcrdbecode(rdb); fprintf(stderr, "put error: %s\n", tcrdberrmsg(ecode)); } tcmapdel(cols); /* search for records */ qry = tcrdbqrynew(rdb); tcrdbqryaddcond(qry, "age", RDBQCNUMGE, "20"); tcrdbqryaddcond(qry, "lang", RDBQCSTROR, "ja,en"); tcrdbqrysetorder(qry, "name", RDBQOSTRASC); tcrdbqrysetlimit(qry, 10, 0); res = tcrdbqrysearch(qry); for(i = 0; i < tclistnum(res); i++){ rbuf = tclistval(res, i, &rsiz); cols = tcrdbtblget(rdb, rbuf, rsiz); if(cols){ printf("%s", rbuf); tcmapiterinit(cols); while((name = tcmapiternext2(cols)) != NULL){ printf("\t%s\t%s", name, tcmapget2(cols, name)); } printf("\n"); tcmapdel(cols); } } tclistdel(res); tcrdbqrydel(qry); /* close the connection */ if(!tcrdbclose(rdb)){ ecode = tcrdbecode(rdb); fprintf(stderr, "close error: %s\n", tcrdberrmsg(ecode)); } /* delete the object */ tcrdbdel(rdb); return 0; } tokyotyrant-1.1.40/ext/000077500000000000000000000000001133351147200150045ustar00rootroot00000000000000tokyotyrant-1.1.40/ext/mapreduce.lua000066400000000000000000000022051133351147200174530ustar00rootroot00000000000000-- -- MapReduce implementation by the Lua extension of Tokyo Tyrant -- ---------------------------------------------------------------- -- public functions ---------------------------------------------------------------- -- count words of all records function wordcount(texpr) local targets = nil if texpr and #texpr > 0 then targets = {} table.insert(targets, texpr) end function mapper(key, value, mapemit) for word in string.gmatch(string.lower(value), "%w+") do mapemit(word, 1) end return true end local res = "" function reducer(key, values) res = res .. key .. "\t" .. #values .. "\n" return true end if not _mapreduce(mapper, reducer, targets) then res = nil end return res end ---------------------------------------------------------------- -- private functions ---------------------------------------------------------------- -- call back function when starting function _begin() _log("Lua processor started") _tmpdir_ = "/tmp" end -- call back function when ending function _end() _log("Lua processor finished") end -- END OF FILE tokyotyrant-1.1.40/ext/queue.lua000066400000000000000000000017651133351147200166440ustar00rootroot00000000000000-- -- Queue mechanism by the Lua extension of Tokyo Tyrant -- ---------------------------------------------------------------- -- public functions ---------------------------------------------------------------- -- enqueue a record function enqueue(key, value) local id = _adddouble(key, 1) if not id then return nil end key = string.format("%s\t%012d", key, id) if not _putkeep(key, value) then return nil end return "ok" end -- dequeue a record function dequeue(key, max) max = tonumber(max) if not max or max < 1 then max = 1 end key = string.format("%s\t", key) local keys = _fwmkeys(key, max) local res = "" for i = 1, #keys do local key = keys[i] local value = _get(key) if _out(key) and value then res = res .. value .. "\n" end end return res end -- get the queue size function queuesize(key) key = string.format("%s\t", key) local keys = _fwmkeys(key) return #keys end -- END OF FILE tokyotyrant-1.1.40/ext/senatus.lua000066400000000000000000000225501133351147200171750ustar00rootroot00000000000000-- -- Sample functions of the Lua extension of Tokyo Tyrant -- -- echo back the key and the value function echo(key, value) return key .. "\t" .. value .. "\n" end -- store a record function put(key, value) if not _put(key, value) then return nil end return "ok" end -- store a new record function putkeep(key, value) if not _putkeep(key, value) then return nil end return "ok" end -- concatenate a value at the end of the existing record function putcat(key, value) if not _putcat(key, value) then return nil end return "ok" end -- remove a record function out(key, value) if not _out(key) then return nil end return "ok" end -- retrieve a record function get(key, value) return _get(key) end -- get the size of the value of a record function vsiz(key, value) local vsiz = _vsiz(key) if vsiz < 0 then return nil end return vsiz end -- initialize the iterator function iterinit(key, value) if not _iterinit() then return nil end return "ok" end -- get the next key of the iterator function iternext(key, value) return _iternext() end -- get forward matching keys function fwmkeys(key, value) value = tonumber(value) local keys = _fwmkeys(key, value) local res = "" for i = 1, #keys do res = res .. keys[i] .. "\n" end return res end -- add an integer to a record function addint(key, value) value = tonumber(value) if not value then return nil end return _addint(key, value) end -- add a real number to a record function adddouble(key, value) value = tonumber(value) if not value then return nil end return _adddouble(key, value) end -- remove all records function vanish(key, value) if not _vanish() then return nil end return "ok" end -- get the number of records function rnum(key, value) return _rnum() end -- get the size of the database function size(key, value) return _size() end -- get records whose key matches a pattern function find(key, value) local res = "" function proc(tkey, tvalue) if string.match(tkey, key) then res = res .. tkey .. "\t" .. tvalue .. "\n" end return true end _foreach(proc) return res end -- get records whose key matches a pattern function finddist(key, value) value = tonumber(value) if not value then value = 0 end local res = "" function proc(tkey, tvalue) if _dist(tkey, key) <= value then res = res .. tkey .. "\t" .. tvalue .. "\n" end return true end _foreach(proc) return res end -- store a record into tha stash function stashput(key, value) if not _stashput(key, value) then return nil end return "ok" end -- remove a record of the stash function stashout(key, value) if not _stashout(key) then return nil end return "ok" end -- retrieve a record in the stash function stashget(key, value) return _stashget(key) end -- retrieve all records in the stash function stashlist(key, value) key = tonumber(key) if not key or key < 0 then key = math.pow(2, 31) end local res = "" function proc(tkey, tvalue) if key < 1 then return false end key = key - 1 res = res .. tkey .. "\t" .. tvalue .. "\n" return true end _stashforeach(proc) return res end -- lock an arbitrary key function lock(key, value) if not _lock(key) then return nil end return "ok" end -- unlock an arbitrary key function unlock(key, value) if not _unlock(key) then return nil end return "ok" end -- get the status information function stat(key, value) local msg = "" msg = msg .. "version\t" .. _version .. "\n" msg = msg .. "time\t" .. string.format("%.6f", _time()) .. "\n" msg = msg .. "pid\t" .. _pid .. "\n" msg = msg .. "sid\t" .. _sid .. "\n" msg = msg .. "thid\t" .. _thid .. "\n" msg = msg .. "rnum\t" .. _rnum() .. "\n" msg = msg .. "size\t" .. _size() .. "\n" return msg end -- increment the value as decimal number, store as string function incr(key, value) value = tonumber(value) if not value then return nil end local old = tonumber(_get(key)) if old then value = value + old end if not _put(key, value) then return nil end return value end -- increment the value as decimal number, store as int32 binary function incrint32(key, value) value = tonumber(value) if not value then return nil end local old = _unpack("i", _get(key)) if old and #old == 1 then value = value + old[1] end if not _put(key, _pack("i", value)) then return nil end return value end -- increment the value as decimal number, store as int64 binary function incrint64(key, value) value = tonumber(value) if not value then return nil end local old = _unpack("l", _get(key)) if old and #old == 1 then value = value + old[1] end if not _put(key, _pack("l", value)) then return nil end return value end -- increment the value as decimal number, store as double binary function incrdouble(key, value) value = tonumber(value) if not value then return nil end local old = _unpack("d", _get(key)) if old and #old == 1 then value = value + old[1] end if not _put(key, _pack("d", value)) then return nil end return value end -- call a versatile function of the misc function function misc(key, value) local args = {} for arg in string.gmatch(value, "[^\t\n]+") do table.insert(args, arg) end local res = _misc(key, args) if not res then return nil end local rbuf = "" for i = 1, #res do rbuf = rbuf .. res[i] .. "\n" end return rbuf end -- split a string function split(key, value) if #value < 1 then value = nil end local elems = _split(key, value) local res = "" for i = 1, #elems do res = res .. elems[i] .. "\n" end return res end -- encode or decode a string function codec(key, value) if key == "ucs" then local ary = _ucs(value) return _pack("n*", ary) elseif key == "~ucs" then local ary = _unpack("n*", value) return _ucs(ary) end return _codec(key, value) end -- get the hash value function hash(key, value) return _hash(key, value) end -- get the bitwise-and of two integers function bitand(key, value) key = tonumber(key) value = tonumber(value) if not key or not value then return nil end return _bit("and", key, value) end -- check substring matching function strstr(key, value) return tostring(_strstr(key, value)) end -- check matching with regular expressions function regex(key, value) return tostring(_regex(key, value)) end -- calculate the edit distance function dist(key, value) return _dist(key, value) end -- get the currnet time function time(key, value) return string.format("%.6f",_time()) end -- sleep and notice function sleep(key, value) key = tonumber(key) if not key or key < 0 then return nil end _sleep(key) return "ok" end -- get status of a file function statfile(key, value) local stat = _stat(key) if not stat then return nil end local res = "" for tkey, tval in pairs(stat) do res = res .. tkey .. "\t" .. tostring(tval) .. "\n" end return res end -- find pathnames matching a pattern function glob(key, value) local paths = _glob(key) local res = "" for i = 1, #paths do res = res .. paths[i] .. "\n" end return res end -- remove a file or a directory and its sub ones recursively function remove(key, value) if not _remove(key) then return nil end return "ok" end -- create a directory function mkdir(key, value) if not _mkdir(key) then return nil end return "ok" end -- evaluate a string function eval(key, value) if not _eval(key) then return nil end return "ok" end -- log a string function log(key, value) local level = tonumber(value) if not level then level = 1 end _log(key, level) return "ok" end -- log the current time function logtime(key, value) _log("current time: " .. _time()) end -- remove expired records of a table database function expire() local args = {}; local cdate = string.format("%d", _time()) table.insert(args, "addcond\0x\0NUMLE\0" .. cdate) table.insert(args, "out") local res = _misc("search", args) if not res then _log("expiration was failed", 2) end end -- remove expired records of a table database function expire2() local args = {}; local xdate = string.format("%d", _time() - 3600) table.insert(args, "addcond\0d\0NUMLE\0" .. xdate) table.insert(args, "out") local res = _misc("search", args) if not res then _log("expiration was failed", 2) end end -- defrag the database file function defrag() local time = tonumber(os.date("%H%M")) if time >= 2230 or time <= 0130 then return nil end if not _misc("defrag") then _log("defragmentation was failed", 2) end end ---------------------------------------------------------------- -- private functions ---------------------------------------------------------------- -- call back function when starting function _begin() _log("Lua processor started") end -- call back function when ending function _end() _log("Lua processor finished") end -- END OF FILE tokyotyrant-1.1.40/ext/usherette.lua000066400000000000000000000221471133351147200175250ustar00rootroot00000000000000-- -- Inverted index by the Lua extension of Tokyo Tyrant -- -- constants DELIMS = " \t\r\n" -- delimiters of tokenizing LIMNUM = 2000 -- limit number of kept occurrence DEFMAX = 10 -- default maximum number of search IDPREFIX = "\t#" -- prefix to get the ID from the URI URIPREFIX = "\t@" -- prefix to get the URI from the ID TEXTPREFIX = "\t|" -- prefix of get the text from the ID ---------------------------------------------------------------- -- public functions ---------------------------------------------------------------- -- register a record with the ID and the text function put(id, text) id = tonumber(id) if not id or id < 1 then return nil end if not text then return nil end if not _putimpl(id, text) then return nil end return "ok" end -- remove a record with the ID and the text function out(id, text) id = tonumber(id) if not id or id < 1 then return nil end if not text then return nil end if not _outimpl(id, text) then return nil end return "ok" end -- replace a record with the ID and the text before and the text after function replace(id, befaft) id = tonumber(id) if not id or id < 1 then return nil end if not befaft then return nil end local pivot = string.find(befaft, "\n", 1, true) if not pivot then return nil end local bef = string.sub(befaft, 1, pivot - 1) local aft = string.sub(befaft, pivot + 1) if not _outimpl(id, bef) then return nil end if not _putimpl(id, aft) then return nil end return "ok" end -- search with a phrase of intersection function search(phrase, max) if not phrase then return nil end max = tonumber(max) if not max or max < 0 then max = DEFMAX end local result = _searchimpl(phrase, false) local rtxt = #result .. "\n" local bot = #result - max + 1 if bot < 1 then bot = 1 end for i = #result, bot, -1 do rtxt = rtxt .. result[i] .. "\n" end return rtxt end -- register a record with the URI and the text function xput(uri, text) if not uri or #uri < 1 or not text then return nil end if not _xputimpl(uri, text) then return nil end return "ok" end -- remove a record with the URI function xout(uri) if not uri or #uri < 1 then return nil end if not _xoutimpl(uri) then return nil end return "ok" end -- retrieve a record with the URI function xget(uri) if not uri or #uri < 1 then return nil end local id = _get(IDPREFIX .. uri) id = tonumber(id) if not id then return nil end local text = _get(TEXTPREFIX .. id) if not text then return nil end return text end -- search for URIs with a phrase of intersection function xsearch(phrase, max) if not phrase then return nil end max = tonumber(max) if not max or max < 0 then max = DEFMAX end local result = _searchimpl(phrase, false) local rtxt = #result .. "\n" for i = #result, 1, -1 do if max < 1 then break end local uri = _get(URIPREFIX .. result[i]) if uri then rtxt = rtxt .. uri .. "\n" max = max - 1 end end return rtxt end -- make the stash of suggestion words function stashmake(minhit) minhit = tonumber(minhit) if not minhit or minhit < 0 then minhit = 0 end _stashvanish() function proc(word, idsel) if not string.match(word, "\t") then local result = _unpack("w*", idsel) if #result >= minhit then _stashput(word, #result) end end return true end _foreach(proc) return "ok" end -- retrieve similar words in the stash function stashlist(word, max) max = tonumber(max) if not max or max < 0 then max = DEFMAX end local result = {} function proc(tword, hnum) local dist = _dist(tword, word, true) if dist <= 3 then table.insert(result, { tword, dist, tonumber(hnum) }) end return true end _stashforeach(proc) function reccmp(a, b) if a[2] > b[2] then return true end if a[2] == b[2] and a[3] < b[3] then return true end return false end table.sort(result, reccmp) local rtxt = "" local bot = #result - max + 1 if bot < 1 then bot = 1 end for i = #result, bot, -1 do local rec = result[i] rtxt = rtxt .. rec[1] .. "\t" .. rec[2] .. "\t" .. rec[3] .. "\n" end return rtxt end ---------------------------------------------------------------- -- private functions ---------------------------------------------------------------- -- call back function when starting function _begin() _log("Lua processor started") end -- call back function when ending function _end() _log("Lua processor finished") end -- register a record with the ID and the text function _putimpl(id, text) local tokens = _tokenize(text) if math.random() < 5 / LIMNUM then for i = 1, #tokens do token = tokens[i] if not _lock(token) then _log("lock error") return false end local ids = {} local idsel = _get(token) if idsel then ids = _unpack("w*", idsel) end local nids = {} local top = #ids - LIMNUM + 2 if top < 1 then top = 1 end for j = top, #ids do table.insert(nids, ids[j]) end table.insert(nids, id) idsel = _pack("w*", nids) if not _put(token, idsel) then _log("put error") _unlock(token) return false end _unlock(token) end else local idsel = _pack("w", id) for i = 1, #tokens do token = tokens[i] if not _lock(token) then _log("lock error") return false end if not _putcat(token, idsel) then _log("putcat error") _unlock(token) return false end _unlock(token) end end return true end -- remove a record with the ID and the text function _outimpl(id, text) local tokens = _tokenize(text) for i = 1, #tokens do token = tokens[i] if not _lock(token) then _log("lock error") return false end local ids = {} local idsel = _get(token) if idsel then ids = _unpack("w*", idsel) end local nids = {} for j = 1, #ids do if ids[j] ~= id then table.insert(nids, ids[j]) end end idsel = _pack("w*", nids) if not _put(token, idsel) then _log("put error") _unlock(token) return false end _unlock(token) end return true end -- search with a phrase of intersection function _searchimpl(phrase, union) local tokens = _tokenize(phrase) local tnum = #tokens if tnum < 1 then return {} end local idsel = _get(tokens[1]) local result = _unpack("w*", idsel) if union then local rsets = {} table.insert(rsets, result) for i = 2, tnum do idsel = _get(tokens[i]) local ids = _unpack("w*", idsel) table.insert(rsets, ids) end result = _union(rsets) else for i = 2, tnum do idsel = _get(tokens[i]) local ids = _unpack("w*", idsel) result = _isect(result, ids) end end table.sort(result) return result end -- register a record with the URI and the text function _xputimpl(uri, text) local id = _get(IDPREFIX .. uri) id = tonumber(id) if id then local otext = _get(TEXTPREFIX .. id) if otext and not _outimpl(id, otext) then return false end else id = _addint(IDPREFIX, 1) if not id then _log("addint error") return false end end if not _putimpl(id, text) then return false end if not _put(IDPREFIX .. uri, id) then _log("put error") return false end if not _put(URIPREFIX .. id, uri) then _log("put error") return false end if not _put(TEXTPREFIX .. id, text) then _log("put error") return false end return true end -- remove a record with the URI function _xoutimpl(uri) local id = _get(IDPREFIX .. uri) id = tonumber(id) if not id then return true end local text = _get(TEXTPREFIX .. id) if not text then return true end if not _outimpl(id, text) then return false end if not _out(IDPREFIX .. uri) then _log("out error") return false end if not _out(URIPREFIX .. id) then _log("out error") return false end if not _out(TEXTPREFIX .. id) then _log("out error") return false end return true end -- break a text into an array of tokens function _tokenize(text) local tokens = {} local uniq = {} for token in string.gmatch(text, "[^" .. DELIMS .. "]+") do if #token > 0 and not uniq[token] then table.insert(tokens, token) uniq[token] = true end end return tokens end -- END OF FILE tokyotyrant-1.1.40/lab/000077500000000000000000000000001133351147200147425ustar00rootroot00000000000000tokyotyrant-1.1.40/lab/datechange000077500000000000000000000023751133351147200167620ustar00rootroot00000000000000#! /bin/sh #================================================================ # datechange # Replace date expressions #================================================================ # set variables LANG=C LC_ALL=C export LANG LC_ALL year=`date '+%Y'` date=`date '+%Y-%m-%d'` fulldate=`date -R` regexsrc='\.(h|c|cc|cpp|cxx|java|pl|pm|pod|rb|rd|lua)$' regexman='\.[0-9]$' regexhtml='\.html$' # edit source files find . -type f | egrep "$regexsrc" | while read file do echo "$file" sed "/opyright/ s/\\(20[0-9][0-9]\\)-\\(20[0-9][0-9]\\)/\\1-$year/" "$file" > "$file.tmp" mv -f "$file.tmp" "$file" done # edit manual files find . -type f | egrep "$regexman" | while read file do echo "$file" sed "/\\.TH/ s/\\(20[0-9][0-9]\\)-\\([0-9][0-9]\\)-\\([0-9][0-9]\\)/$date/" "$file" > "$file.tmp" mv -f "$file.tmp" "$file" done # edit HTML files find . -type f | egrep "$regexhtml" | while read file do echo "$file" sed -e "/opyright/ s/\\(20[0-9][0-9]\\)-\\(20[0-9][0-9]\\)/\\1-$year/" -e "/
/ s/Last Update: *\\([A-Z][a-z][a-z], [0-9][0-9] [A-Z][a-z][a-z] 20[0-9][0-9] [0-9][0-9]:[0-9][0-9]:[0-9][0-9] +[0-9][0-9]*\\)/Last Update: $fulldate/" "$file" > "$file.tmp" mv -f "$file.tmp" "$file" done # exit normally exit 0 # END OF FILE tokyotyrant-1.1.40/lab/diffcheck000077500000000000000000000014361133351147200166020ustar00rootroot00000000000000#! /bin/sh #================================================================ # diffcheck # List files different from ones of another version #================================================================ # set variables LANG=C LC_ALL=C export LANG LC_ALL regex='\.(h|c|cc|cpp|cxx|java|pl|pm|pod|rb|rd|lua|[1-9]|html|txt)$' # check arguments if [ $# != 1 ] then printf 'diffcheck: usage: diffcheck directory_of_oldversion\n' 1>&2 exit 1 fi # diff files find . -type f | egrep $regex | while read file do old=`printf '%s\n' "$file" | sed 's/^\.\///'` printf 'Checking %s and %s ... ' "$file" "$1/$old" res=`diff -q "$file" "$1/$old"` if [ -z "$res" ] then printf 'same\n' else printf '### !!! DIFFERENT !!! ###\n' fi done # exit normally exit 0 # END OF FILE tokyotyrant-1.1.40/lab/fibonacci.lua000066400000000000000000000007611133351147200173660ustar00rootroot00000000000000function fibonacci(key, value) local num = tonumber(key) local large = math.pow((1 + math.sqrt(5)) / 2, num) local small = math.pow((1 - math.sqrt(5)) / 2, num) return (large - small) / math.sqrt(5) end function fibnext(key, value) local cur = tonumber(_get("fibcur")) if not cur then _put("fibold", 0) _put("fibcur", 1) return 1 end local old = tonumber(_get("fibold")) _put("fibold", cur) cur = old + cur _put("fibcur", cur) return cur end tokyotyrant-1.1.40/lab/footprint.lua000066400000000000000000000030321133351147200174670ustar00rootroot00000000000000MAXPRINT = 60 function add(key, value) key = tonumber(key) value = tonumber(value) if not key or not value or key == value then return nil end local ksel = _pack("i", key) local time = os.time() local date = os.date("*t", time) date.hour = 0 date.min = 0 date.sec = 0 local mintime = os.time(date) local vsel local ary = _unpack("i*", _get(ksel)) local anum = 1 if ary and #ary > 0 then local nary = {} local nidx = 1 local nidxmax = MAXPRINT * 2 - 1 for i = 1, #ary, 2 do if ary[i] ~= value or ary[i+1] < mintime then nary[nidx] = ary[i] nary[nidx+1] = ary[i+1] nidx = nidx + 2 end end vsel = _pack("i*", nary, value, time) anum = (#nary / 2) + 1 if anum > MAXPRINT then vsel = string.sub(vsel, MAXPRINT * -8) anum = MAXPRINT end else vsel = _pack("ii", value, time) end if not _put(ksel, vsel) then return nil end return anum end function list(key, value) key = tonumber(key) value = tonumber(value) if not key then return nil end if not value or value < 1 then value = MAXPRINT end local result = "" local ksel = _pack("i", key) local ary = _unpack("i*", _get(ksel)) if ary and #ary > 0 then for i = #ary - 1, 0, -2 do if value < 1 then break end result = result .. ary[i] .. "\t" .. ary[i+1] .. "\n" value = value - 1 end end return result end tokyotyrant-1.1.40/lab/highlow.lua000066400000000000000000000041221133351147200171050ustar00rootroot00000000000000NUMRANGE = 100 MAXROUND = 5 math.randomseed(os.time()) function start(key, value) value = tonumber(value) if not value or value <= 0 then return "error: invalid value" end if not _putkeep(key .. ":r", 1) then return "error: already started" end local num = math.random(NUMRANGE) _put(key .. ":m", value) _put(key .. ":n", num) return "Welcome, " .. key .. ".\n" .. "The current number is " .. num .. ".\n" .. "Your money is " .. value .. ".\n" .. "Round 1 Bet!\n" end function high(key, value) return do_bet(key, value, true) end function low(key, value) return do_bet(key, value, false) end function over(key, value) if _vsiz(key .. ":r") < 0 then return "error: not started" end _out(key .. ":r") _out(key .. ":m") _out(key .. ":n") return "Good Bye!" end function do_bet(key, value, ishigh) value = tonumber(value) if not value or value <= 0 then return "error: invalid value" end local round = tonumber(_get(key .. ":r")) if not round then return "error: not started" end local money = tonumber(_get(key .. ":m")) if round > MAXROUND or money < 1 then return "error: already finished" end if value > money then value = money end local num = tonumber(_get(key .. ":n")) local newnum = math.random(NUMRANGE) local cmp = "even" local res = "lost" if newnum > num then cmp = "high" if ishigh then res = "won" end elseif newnum < num then cmp = "low" if not ishigh then res = "won" end end round = round + 1 if res == "won" then money = money + value else money = money - value end _put(key .. ":r", round) _put(key .. ":m", money) _put(key .. ":n", newnum) local call = "Round " .. round .. " Bet!\n" if round > MAXROUND or money < 1 then call = "Game Over!\n" end return "The currnet number is " .. newnum .. ".\n" .. newnum .. ":" .. num .. " (" .. cmp .. ") ... You " .. res .. "!\n" .. "Your money is " .. money .. ".\n" .. call end tokyotyrant-1.1.40/lab/killdualmaster000077500000000000000000000005321133351147200177050ustar00rootroot00000000000000#! /bin/sh export PATH="$PATH:/usr/local/bin:.:.." export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/local/lib:.:.." [ -f 1978.pid ] && pid1978=`cat 1978.pid` [ -f 1979.pid ] && pid1979=`cat 1979.pid` [ -n "$pid1978" ] && kill $pid1978 && printf 'killed: %d\n' $pid1978 [ -n "$pid1979" ] && kill $pid1979 && printf 'killed: %d\n' $pid1979 exit 1 tokyotyrant-1.1.40/lab/rundualmaster000077500000000000000000000012361133351147200175600ustar00rootroot00000000000000#! /bin/sh export PATH="$PATH:/usr/local/bin:.:.." export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/local/lib:.:.." rm -rf 1978-ulog mkdir -p 1978-ulog rm -f 1978.* ttserver -port 1978 -dmn \ -pid "$PWD/1978.pid" \ -log "$PWD/1978.log" \ -ulog "$PWD/1978-ulog" \ -sid 1978 \ -mhost localhost \ -mport 1979 \ -rts "$PWD/1978.rts" \ "$PWD/1978.tch" rm -rf 1979-ulog mkdir -p 1979-ulog rm -f 1979.* rm -rf 1979-ulog mkdir -p 1979-ulog rm -f 1979.* ttserver -port 1979 -dmn \ -pid "$PWD/1979.pid" \ -log "$PWD/1979.log" \ -ulog "$PWD/1979-ulog" \ -sid 1979 \ -mhost localhost \ -mport 1978 \ -rts "$PWD/1979.rts" \ "$PWD/1979.tch" exit 0 tokyotyrant-1.1.40/lab/stepcount000077500000000000000000000006621133351147200167200ustar00rootroot00000000000000#! /bin/sh #================================================================ # stepcount # Find files including dispensable tab characters #================================================================ # set variables LANG=C LC_ALL=C export LANG LC_ALL regex='(\.h|\.c|\.cc|\.java|\.pl|\.pm|\.xs|\.rb|\.js)$' # count steps files=`find . -type f | egrep $regex` wc -l $files | sort -n # exit normally exit 0 # END OF FILE tokyotyrant-1.1.40/lab/tabcheck000077500000000000000000000013521133351147200164350ustar00rootroot00000000000000#! /bin/sh #================================================================ # tabcheck # Find files including dispensable tab and space characters #================================================================ # set variables LANG=C LC_ALL=C export LANG LC_ALL regex='\.(h|c|cc|cpp|cxx|java|pl|pm|pod|rb|rd|lua)$' tabcode=`printf '\t'` # find tab find . -type f | egrep $regex | while read file do printf 'Checking %s ... ' $file err=0 if grep "$tabcode" $file > /dev/null then printf '### !!! TAB FOUND !!! ###' err=1 fi if grep ' $' $file > /dev/null then printf '### !!! TAILING SPACE FOUND !!! ###' err=1 fi [ "$err" = 0 ] && printf 'ok' printf '\n' done # exit normally exit 0 # END OF FILE tokyotyrant-1.1.40/lab/ushrtregister.pl000066400000000000000000000023261133351147200202140ustar00rootroot00000000000000#! /usr/bin/perl use strict; use warnings; use constant { DICTFILE => '/usr/share/dict/words', HOSTNAME => 'localhost', PORT => 1978, TEXTNUM => 10000, WORDNUM => 1000, }; my @words; my %uniq; my $ifp; open($ifp, '<' . DICTFILE) || die("could not open the dictionary"); while(defined(my $word = <$ifp>)){ chomp($word); $word =~ tr/A-Z/a-z/; next if($word =~ /[^a-z]/); next if(length($word) < 2); if($word =~ /s$/){ my $singular = $word; $singular =~ s/s$//; next if(defined($uniq{$singular})); } my $num = 100 / length($word) + 1; for(my $i = 0; $i < $num; $i++){ push(@words, $word); } $uniq{$word} = 1; } close($ifp); my $rnum = scalar(@words); $ENV{'PATH'} = $ENV{'PATH'} . ":."; $| = 1; printf("start\n"); for(my $i = 1; $i <= TEXTNUM; $i++){ printf("."); my $text = $words[int(rand($rnum))]; my $wnum = int(rand(WORDNUM * 2)); for(my $j = 1; $j < $wnum; $j++){ $text .= ' ' . $words[int(rand($rnum))]; } my $cmd = sprintf("tcrmgr ext -port %d %s put %d '%s'", PORT, HOSTNAME, $i, $text); system($cmd . ">/dev/null"); printf(" (%d)\n", $i) if($i % 50 == 0); } printf("finish\n"); # END OF FILE tokyotyrant-1.1.40/lab/widthcheck000077500000000000000000000020401133351147200170010ustar00rootroot00000000000000#! /usr/bin/ruby -w #================================================================ # widthcheck # Find files including too wide lines #================================================================ LIMITWIDTH = 97 def checkfile(path) printf("Checking: %s\n", path) ok = true open(path, "r") do |file| num = 0 file.each do |line| num += 1 line.chomp! if line.length > LIMITWIDTH && !line.index("/*") && !line.index("*/") printf("FOUND: %s: %d: %d: %s\n", path, num, line.length, line); ok = false end end end return ok end ok = true list = Array::new(ARGV) list.push(".") if list.empty? while !list.empty? path = list.shift begin if File::ftype(path) == "directory" Dir.entries(path).each do |cpath| if cpath != "." and cpath != ".." list.push(path + "/" + cpath) end end else ok = false if !checkfile(path) end rescue end end if ok printf("ALL OK\n") exit(0) else printf("ERROR\n") exit(1) end # END OF FILE tokyotyrant-1.1.40/man/000077500000000000000000000000001133351147200147575ustar00rootroot00000000000000tokyotyrant-1.1.40/man/htmltoman000077500000000000000000000043461133351147200167170ustar00rootroot00000000000000#! /usr/bin/awk -f function strip(text){ gsub("^ *<[a-zA-Z0-9]*[^>]*>", "", text) gsub(" *$", "", text) return text } function unescape(text){ gsub("<", "<", text) gsub(">", ">", text) gsub(""", "\"", text) gsub("&", "\\&", text) gsub("-", "\\-", text) return text } BEGIN { date = strftime("%Y-%m-%d") printf(".TH \"%s\" %d \"%s\" \"%s\" \"%s\"\n\n", "INTRO", 3, date, "Man Page", "Tokyo Cabinet") } / *]*>.*<\/h[1-3]> *$/ { text = $0 text = strip(text) text = unescape(text) text = toupper(text) printf("\n") printf(".SH %s\n", text) } / *

]*>.*<\/p> *$/ { text = $0 text = strip(text) text = gensub("]*>([^<]*)", "\\\\fB\\1\\\\fR", "g", text) text = gensub("]*>([^<]*)", "\\\\fI\\1\\\\fR", "g", text) gsub("<[^>]*>", "", text) text = unescape(text) printf(".PP\n") printf("%s\n", text) } / *

]*> *$/ { printf(".PP\n") printf(".RS\n") } / *<\/dl> *$/ { printf(".RE\n") } / *
]*>.*<\/dt> *$/ { text = $0 text = strip(text) text = gensub("]*>([^<]*)", "\\\\fI\\1\\\\fB", "g", text) gsub("<[^>]*>", "", text) gsub("[\\||\\[|\\]]", "\\fR&\\fB", text) text = unescape(text) printf(".br\n") printf("\\fB%s\\fR\n", text) } / *
]*>.*<\/dd> *$/ { text = $0 text = strip(text) text = gensub("]*>([^<]*)", "\\\\fB\\1\\\\fR", "g", text) text = gensub("]*>([^<]*)", "\\\\fI\\1\\\\fR", "g", text) gsub("<[^>]*>", "", text) text = unescape(text) printf(".RS\n") printf("%s\n", text) printf(".RE\n") } / *
    ]*> *$/ { printf(".PP\n") printf(".RS\n") } / *<\/ul> *$/ { printf(".RE\n") } / *
  • ]*>.*<\/li> *$/ { text = $0 text = strip(text) text = gensub("]*>(.*)", "\\\\fB\\1\\\\fR", "g", text) text = gensub("]*>([^<]*)", "\\\\fI\\1\\\\fR", "g", text) gsub("<[^>]*>", "", text) text = unescape(text) printf("%s\n", text) printf(".br\n") } END { printf("\n") printf(".SH SEE ALSO\n") printf(".PP\n") printf(".BR ttutil (3),\n") printf(".BR tcrdb (3)\n") printf(".PP\n") printf("Please see\n") printf(".I http://1978th.net/tokyotyrant/\n") printf("for detail.\n") } tokyotyrant-1.1.40/man/tcrdb.3000066400000000000000000001204331133351147200161440ustar00rootroot00000000000000.TH "TCRDB" 3 "2010-01-20" "Man Page" "Tokyo Tyrant" .SH NAME tcrdb \- the remote database API .SH DESCRIPTION .PP Remote database is a set of interfaces to use an abstract database of Tokyo Cabinet, mediated by a server of Tokyo Tyrant. .PP .PP To use the remote database API, include `\fBtcrdb.h\fR' and related standard header files. Usually, write the following description near the front of a source file. .PP .RS .br \fB#include \fR .br \fB#include \fR .br \fB#include \fR .br \fB#include \fR .RE .PP Objects whose type is pointer to `\fBTCRDB\fR' are used to handle remote databases. a remote database object is created with the function `\fBtcrdbnew\fR' and is deleted with the function `\fBtcrdbdel\fR'. To avoid memory leak, it is important to delete every object when it is no longer in use. .PP Before operations to store or retrieve records, it is necessary to connect the remote database object to the server. The function `\fBtcrdbopen\fR' is used to open a database connection and the function `\fBtcrdbclose\fR' is used to close the connection. .SH DESCRIPTION .PP The function `tcrdberrmsg' is used in order to get the message string corresponding to an error code. .PP .RS .br \fBconst char *tcrdberrmsg(int \fIecode\fB);\fR .RS `\fIecode\fR' specifies the error code. .RE .RS The return value is the message string of the error code. .RE .RE .PP The function `tcrdbnew' is used in order to create a remote database object. .PP .RS .br \fBTCRDB *tcrdbnew(void);\fR .RS The return value is the new remote database object. .RE .RE .PP The function `tcrdbdel' is used in order to delete a remote database object. .PP .RS .br \fBvoid tcrdbdel(TCRDB *\fIrdb\fB);\fR .RS `\fIrdb\fR' specifies the remote database object. .RE .RE .PP The function `tcrdbecode' is used in order to get the last happened error code of a remote database object. .PP .RS .br \fBint tcrdbecode(TCRDB *\fIrdb\fB);\fR .RS `\fIrdb\fR' specifies the remote database object. .RE .RS The return value is the last happened error code. .RE .RS The following error code is defined: `TTESUCCESS' for success, `TTEINVALID' for invalid operation, `TTENOHOST' for host not found, `TTEREFUSED' for connection refused, `TTESEND' for send error, `TTERECV' for recv error, `TTEKEEP' for existing record, `TTENOREC' for no record found, `TTEMISC' for miscellaneous error. .RE .RE .PP The function `tcrdbtune' is used in order to set the tuning parameters of a hash database object. .PP .RS .br \fBbool tcrdbtune(TCRDB *\fIrdb\fB, double \fItimeout\fB, int \fIopts\fB);\fR .RS `\fIrdb\fR' specifies the remote database object. .RE .RS `\fItimeout\fR' specifies the timeout of each query in seconds. If it is not more than 0, the timeout is not specified. .RE .RS `\fIopts\fR' specifies options by bitwise\-or: `RDBTRECON' specifies that the connection is recovered automatically when it is disconnected. .RE .RS If successful, the return value is true, else, it is false. .RE .RS Note that the tuning parameters should be set before the database is opened. .RE .RE .PP The function `tcrdbopen' is used in order to open a remote database. .PP .RS .br \fBbool tcrdbopen(TCRDB *\fIrdb\fB, const char *\fIhost\fB, int \fIport\fB);\fR .RS `\fIrdb\fR' specifies the remote database object. .RE .RS `\fIhost\fR' specifies the name or the address of the server. .RE .RS `\fIport\fR' specifies the port number. If it is not more than 0, UNIX domain socket is used and the path of the socket file is specified by the host parameter. .RE .RS If successful, the return value is true, else, it is false. .RE .RE .PP The function `tcrdbopen2' is used in order to open a remote database with a simple server expression. .PP .RS .br \fBbool tcrdbopen2(TCRDB *\fIrdb\fB, const char *\fIexpr\fB);\fR .RS `\fIrdb\fR' specifies the remote database object. .RE .RS `\fIexpr\fR' specifies the simple server expression. It is composed of two substrings separated by ":". The former field specifies the name or the address of the server. The latter field specifies the port number. If the latter field is omitted, the default port number is specified. .RE .RS If successful, the return value is true, else, it is false. .RE .RE .PP The function `tcrdbclose' is used in order to close a remote database object. .PP .RS .br \fBbool tcrdbclose(TCRDB *\fIrdb\fB);\fR .RS `\fIrdb\fR' specifies the remote database object. .RE .RS If successful, the return value is true, else, it is false. .RE .RE .PP The function `tcrdbput' is used in order to store a record into a remote database object. .PP .RS .br \fBbool tcrdbput(TCRDB *\fIrdb\fB, const void *\fIkbuf\fB, int \fIksiz\fB, const void *\fIvbuf\fB, int \fIvsiz\fB);\fR .RS `\fIrdb\fR' specifies the remote database object. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS `\fIvbuf\fR' specifies the pointer to the region of the value. .RE .RS `\fIvsiz\fR' specifies the size of the region of the value. .RE .RS If successful, the return value is true, else, it is false. .RE .RS If a record with the same key exists in the database, it is overwritten. .RE .RE .PP The function `tcrdbput2' is used in order to store a string record into a remote object. .PP .RS .br \fBbool tcrdbput2(TCRDB *\fIrdb\fB, const char *\fIkstr\fB, const char *\fIvstr\fB);\fR .RS `\fIrdb\fR' specifies the remote database object. .RE .RS `\fIkstr\fR' specifies the string of the key. .RE .RS `\fIvstr\fR' specifies the string of the value. .RE .RS If successful, the return value is true, else, it is false. .RE .RS If a record with the same key exists in the database, it is overwritten. .RE .RE .PP The function `tcrdbputkeep' is used in order to store a new record into a remote database object. .PP .RS .br \fBbool tcrdbputkeep(TCRDB *\fIrdb\fB, const void *\fIkbuf\fB, int \fIksiz\fB, const void *\fIvbuf\fB, int \fIvsiz\fB);\fR .RS `\fIrdb\fR' specifies the remote database object. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS `\fIvbuf\fR' specifies the pointer to the region of the value. .RE .RS `\fIvsiz\fR' specifies the size of the region of the value. .RE .RS If successful, the return value is true, else, it is false. .RE .RS If a record with the same key exists in the database, this function has no effect. .RE .RE .PP The function `tcrdbputkeep2' is used in order to store a new string record into a remote database object. .PP .RS .br \fBbool tcrdbputkeep2(TCRDB *\fIrdb\fB, const char *\fIkstr\fB, const char *\fIvstr\fB);\fR .RS `\fIrdb\fR' specifies the remote database object. .RE .RS `\fIkstr\fR' specifies the string of the key. .RE .RS `\fIvstr\fR' specifies the string of the value. .RE .RS If successful, the return value is true, else, it is false. .RE .RS If a record with the same key exists in the database, this function has no effect. .RE .RE .PP The function `tcrdbputcat' is used in order to concatenate a value at the end of the existing record in a remote database object. .PP .RS .br \fBbool tcrdbputcat(TCRDB *\fIrdb\fB, const void *\fIkbuf\fB, int \fIksiz\fB, const void *\fIvbuf\fB, int \fIvsiz\fB);\fR .RS `\fIrdb\fR' specifies the remote database object. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS `\fIvbuf\fR' specifies the pointer to the region of the value. .RE .RS `\fIvsiz\fR' specifies the size of the region of the value. .RE .RS If successful, the return value is true, else, it is false. .RE .RS If there is no corresponding record, a new record is created. .RE .RE .PP The function `tcrdbputcat2' is used in order to concatenate a string value at the end of the existing record in a remote database object. .PP .RS .br \fBbool tcrdbputcat2(TCRDB *\fIrdb\fB, const char *\fIkstr\fB, const char *\fIvstr\fB);\fR .RS `\fIrdb\fR' specifies the remote database object. .RE .RS `\fIkstr\fR' specifies the string of the key. .RE .RS `\fIvstr\fR' specifies the string of the value. .RE .RS If successful, the return value is true, else, it is false. .RE .RS If there is no corresponding record, a new record is created. .RE .RE .PP The function `tcrdbputshl' is used in order to concatenate a value at the end of the existing record and shift it to the left. .PP .RS .br \fBbool tcrdbputshl(TCRDB *\fIrdb\fB, const void *\fIkbuf\fB, int \fIksiz\fB, const void *\fIvbuf\fB, int \fIvsiz\fB, int \fIwidth\fB);\fR .RS `\fIrdb\fR' specifies the remote database object. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS `\fIvbuf\fR' specifies the pointer to the region of the value. .RE .RS `\fIvsiz\fR' specifies the size of the region of the value. .RE .RS `\fIwidth\fR' specifies the width of the record. .RE .RS If successful, the return value is true, else, it is false. .RE .RS If there is no corresponding record, a new record is created. .RE .RE .PP The function `tcrdbputshl2' is used in order to concatenate a string value at the end of the existing record and shift it to the left. .PP .RS .br \fBbool tcrdbputshl2(TCRDB *\fIrdb\fB, const char *\fIkstr\fB, const char *\fIvstr\fB, int \fIwidth\fB);\fR .RS `\fIrdb\fR' specifies the remote database object. .RE .RS `\fIkstr\fR' specifies the string of the key. .RE .RS `\fIvstr\fR' specifies the string of the value. .RE .RS `\fIwidth\fR' specifies the width of the record. .RE .RS If successful, the return value is true, else, it is false. .RE .RS If there is no corresponding record, a new record is created. .RE .RE .PP The function `tcrdbputnr' is used in order to store a record into a remote database object without response from the server. .PP .RS .br \fBbool tcrdbputnr(TCRDB *\fIrdb\fB, const void *\fIkbuf\fB, int \fIksiz\fB, const void *\fIvbuf\fB, int \fIvsiz\fB);\fR .RS `\fIrdb\fR' specifies the remote database object. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS `\fIvbuf\fR' specifies the pointer to the region of the value. .RE .RS `\fIvsiz\fR' specifies the size of the region of the value. .RE .RS If successful, the return value is true, else, it is false. .RE .RS If a record with the same key exists in the database, it is overwritten. .RE .RE .PP The function `tcrdbputnr2' is used in order to store a string record into a remote object without response from the server. .PP .RS .br \fBbool tcrdbputnr2(TCRDB *\fIrdb\fB, const char *\fIkstr\fB, const char *\fIvstr\fB);\fR .RS `\fIrdb\fR' specifies the remote database object. .RE .RS `\fIkstr\fR' specifies the string of the key. .RE .RS `\fIvstr\fR' specifies the string of the value. .RE .RS If successful, the return value is true, else, it is false. .RE .RS If a record with the same key exists in the database, it is overwritten. .RE .RE .PP The function `tcrdbout' is used in order to remove a record of a remote database object. .PP .RS .br \fBbool tcrdbout(TCRDB *\fIrdb\fB, const void *\fIkbuf\fB, int \fIksiz\fB);\fR .RS `\fIrdb\fR' specifies the remote database object. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS If successful, the return value is true, else, it is false. .RE .RE .PP The function `tcrdbout2' is used in order to remove a string record of a remote database object. .PP .RS .br \fBbool tcrdbout2(TCRDB *\fIrdb\fB, const char *\fIkstr\fB);\fR .RS `\fIrdb\fR' specifies the remote database object. .RE .RS `\fIkstr\fR' specifies the string of the key. .RE .RS If successful, the return value is true, else, it is false. .RE .RE .PP The function `tcrdbget' is used in order to retrieve a record in a remote database object. .PP .RS .br \fBvoid *tcrdbget(TCRDB *\fIrdb\fB, const void *\fIkbuf\fB, int \fIksiz\fB, int *\fIsp\fB);\fR .RS `\fIrdb\fR' specifies the remote database object. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS `\fIsp\fR' specifies the pointer to the variable into which the size of the region of the return value is assigned. .RE .RS If successful, the return value is the pointer to the region of the value of the corresponding record. `NULL' is returned if no record corresponds. .RE .RS Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use. .RE .RE .PP The function `tcrdbget2' is used in order to retrieve a string record in a remote database object. .PP .RS .br \fBchar *tcrdbget2(TCRDB *\fIrdb\fB, const char *\fIkstr\fB);\fR .RS `\fIrdb\fR' specifies the remote database object. .RE .RS `\fIkstr\fR' specifies the string of the key. .RE .RS If successful, the return value is the string of the value of the corresponding record. `NULL' is returned if no record corresponds. .RE .RS Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use. .RE .RE .PP The function `tcrdbget3' is used in order to retrieve records in a remote database object. .PP .RS .br \fBbool tcrdbget3(TCRDB *\fIrdb\fB, TCMAP *\fIrecs\fB);\fR .RS `\fIrdb\fR' specifies the remote database object. .RE .RS `\fIrecs\fR' specifies a map object containing the retrieval keys. As a result of this function, keys existing in the database have the corresponding values and keys not existing in the database are removed. .RE .RS If successful, the return value is true, else, it is false. .RE .RE .PP The function `tcrdbvsiz' is used in order to get the size of the value of a record in a remote database object. .PP .RS .br \fBint tcrdbvsiz(TCRDB *\fIrdb\fB, const void *\fIkbuf\fB, int \fIksiz\fB);\fR .RS `\fIrdb\fR' specifies the remote database object. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS If successful, the return value is the size of the value of the corresponding record, else, it is \-1. .RE .RE .PP The function `tcrdbvsiz2' is used in order to get the size of the value of a string record in a remote database object. .PP .RS .br \fBint tcrdbvsiz2(TCRDB *\fIrdb\fB, const char *\fIkstr\fB);\fR .RS `\fIrdb\fR' specifies the remote database object. .RE .RS `\fIkstr\fR' specifies the string of the key. .RE .RS If successful, the return value is the size of the value of the corresponding record, else, it is \-1. .RE .RE .PP The function `tcrdbiterinit' is used in order to initialize the iterator of a remote database object. .PP .RS .br \fBbool tcrdbiterinit(TCRDB *\fIrdb\fB);\fR .RS `\fIrdb\fR' specifies the remote database object. .RE .RS If successful, the return value is true, else, it is false. .RE .RS The iterator is used in order to access the key of every record stored in a database. .RE .RE .PP The function `tcrdbiternext' is used in order to get the next key of the iterator of a remote database object. .PP .RS .br \fBvoid *tcrdbiternext(TCRDB *\fIrdb\fB, int *\fIsp\fB);\fR .RS `\fIrdb\fR' specifies the remote database object. .RE .RS `\fIsp\fR' specifies the pointer to the variable into which the size of the region of the return value is assigned. .RE .RS If successful, the return value is the pointer to the region of the next key, else, it is `NULL'. `NULL' is returned when no record is to be get out of the iterator. .RE .RS Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use. The iterator can be updated by multiple connections and then it is not assured that every record is traversed. .RE .RE .PP The function `tcrdbiternext2' is used in order to get the next key string of the iterator of a remote database object. .PP .RS .br \fBchar *tcrdbiternext2(TCRDB *\fIrdb\fB);\fR .RS `\fIrdb\fR' specifies the remote database object. .RE .RS If successful, the return value is the string of the next key, else, it is `NULL'. `NULL' is returned when no record is to be get out of the iterator. .RE .RS Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use. The iterator can be updated by multiple connections and then it is not assured that every record is traversed. .RE .RE .PP The function `tcrdbfwmkeys' is used in order to get forward matching keys in a remote database object. .PP .RS .br \fBTCLIST *tcrdbfwmkeys(TCRDB *\fIrdb\fB, const void *\fIpbuf\fB, int \fIpsiz\fB, int \fImax\fB);\fR .RS `\fIrdb\fR' specifies the remote database object. .RE .RS `\fIpbuf\fR' specifies the pointer to the region of the prefix. .RE .RS `\fIpsiz\fR' specifies the size of the region of the prefix. .RE .RS `\fImax\fR' specifies the maximum number of keys to be fetched. If it is negative, no limit is specified. .RE .RS The return value is a list object of the corresponding keys. This function does never fail. It returns an empty list even if no key corresponds. .RE .RS Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use. .RE .RE .PP The function `tcrdbfwmkeys2' is used in order to get forward matching string keys in a remote database object. .PP .RS .br \fBTCLIST *tcrdbfwmkeys2(TCRDB *\fIrdb\fB, const char *\fIpstr\fB, int \fImax\fB);\fR .RS `\fIrdb\fR' specifies the remote database object. .RE .RS `\fIpstr\fR' specifies the string of the prefix. .RE .RS `\fImax\fR' specifies the maximum number of keys to be fetched. If it is negative, no limit is specified. .RE .RS The return value is a list object of the corresponding keys. This function does never fail. It returns an empty list even if no key corresponds. .RE .RS Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use. .RE .RE .PP The function `tcrdbaddint' is used in order to add an integer to a record in a remote database object. .PP .RS .br \fBint tcrdbaddint(TCRDB *\fIrdb\fB, const void *\fIkbuf\fB, int \fIksiz\fB, int \fInum\fB);\fR .RS `\fIrdb\fR' specifies the remote database object. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS `\fInum\fR' specifies the additional value. .RE .RS If successful, the return value is the summation value, else, it is `INT_MIN'. .RE .RS If the corresponding record exists, the value is treated as an integer and is added to. If no record corresponds, a new record of the additional value is stored. .RE .RE .PP The function `tcrdbadddouble' is used in order to add a real number to a record in a remote database object. .PP .RS .br \fBdouble tcrdbadddouble(TCRDB *\fIrdb\fB, const void *\fIkbuf\fB, int \fIksiz\fB, double \fInum\fB);\fR .RS `\fIrdb\fR' specifies the remote database object. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS `\fInum\fR' specifies the additional value. .RE .RS If successful, the return value is the summation value, else, it is Not\-a\-Number. .RE .RS If the corresponding record exists, the value is treated as a real number and is added to. If no record corresponds, a new record of the additional value is stored. .RE .RE .PP The function `tcrdbext' is used in order to call a function of the script language extension. .PP .RS .br \fBvoid *tcrdbext(TCRDB *\fIrdb\fB, const char *\fIname\fB, int \fIopts\fB, const void *\fIkbuf\fB, int \fIksiz\fB, const void *\fIvbuf\fB, int \fIvsiz\fB, int *\fIsp\fB);\fR .RS `\fIrdb\fR' specifies the remote database object. .RE .RS `\fIname\fR' specifies the function name. .RE .RS `\fIopts\fR' specifies options by bitwise\-or: `RDBXOLCKREC' for record locking, `RDBXOLCKGLB' for global locking. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS `\fIvbuf\fR' specifies the pointer to the region of the value. .RE .RS `\fIvsiz\fR' specifies the size of the region of the value. .RE .RS `\fIsp\fR' specifies the pointer to the variable into which the size of the region of the return value is assigned. .RE .RS If successful, the return value is the pointer to the region of the value of the response. `NULL' is returned on failure. .RE .RS Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use. .RE .RE .PP The function `tcrdbext2' is used in order to call a function of the script language extension with string parameters. .PP .RS .br \fBchar *tcrdbext2(TCRDB *\fIrdb\fB, const char *\fIname\fB, int \fIopts\fB, const char *\fIkstr\fB, const char *\fIvstr\fB);\fR .RS `\fIrdb\fR' specifies the remote database object. .RE .RS `\fIname\fR' specifies the function name. .RE .RS `\fIopts\fR' specifies options by bitwise\-or: `RDBXOLCKREC' for record locking, `RDBXOLCKGLB' for global locking. .RE .RS `\fIkstr\fR' specifies the string of the key. .RE .RS `\fIvstr\fR' specifies the string of the value. .RE .RS If successful, the return value is the string of the value of the response. `NULL' is returned on failure. .RE .RS Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use. .RE .RE .PP The function `tcrdbsync' is used in order to synchronize updated contents of a remote database object with the file and the device. .PP .RS .br \fBbool tcrdbsync(TCRDB *\fIrdb\fB);\fR .RS `\fIrdb\fR' specifies the remote database object. .RE .RS If successful, the return value is true, else, it is false. .RE .RE .PP The function `tcrdboptimize' is used in order to optimize the storage of a remove database object. .PP .RS .br \fBbool tcrdboptimize(TCRDB *\fIrdb\fB, const char *\fIparams\fB);\fR .RS `\fIrdb\fR' specifies the remote database object. .RE .RS `\fIparams\fR' specifies the string of the tuning parameters. If it is `NULL', it is not used. .RE .RS If successful, the return value is true, else, it is false. .RE .RE .PP The function `tcrdbvanish' is used in order to remove all records of a remote database object. .PP .RS .br \fBbool tcrdbvanish(TCRDB *\fIrdb\fB);\fR .RS `\fIrdb\fR' specifies the remote database object. .RE .RS If successful, the return value is true, else, it is false. .RE .RE .PP The function `tcrdbcopy' is used in order to copy the database file of a remote database object. .PP .RS .br \fBbool tcrdbcopy(TCRDB *\fIrdb\fB, const char *\fIpath\fB);\fR .RS `\fIrdb\fR' specifies the remote database object. .RE .RS `\fIpath\fR' specifies the path of the destination file. If it begins with `@', the trailing substring is executed as a command line. .RE .RS If successful, the return value is true, else, it is false. False is returned if the executed command returns non\-zero code. .RE .RS The database file is assured to be kept synchronized and not modified while the copying or executing operation is in progress. So, this function is useful to create a backup file of the database file. .RE .RE .PP The function `tcrdbrestore' is used in order to restore the database file of a remote database object from the update log. .PP .RS .br \fBbool tcrdbrestore(TCRDB *\fIrdb\fB, const char *\fIpath\fB, uint64_t \fIts\fB, int \fIopts\fB);\fR .RS `\fIrdb\fR' specifies the remote database object. .RE .RS `\fIpath\fR' specifies the path of the update log directory. .RE .RS `\fIopts\fR' specifies options by bitwise\-or: `RDBROCHKCON' for consistency checking. .RE .RS `\fIts\fR' specifies the beginning time stamp in microseconds. .RE .RS If successful, the return value is true, else, it is false. .RE .RE .PP The function `tcrdbsetmst' is used in order to set the replication master of a remote database object. .PP .RS .br \fBbool tcrdbsetmst(TCRDB *\fIrdb\fB, const char *\fIhost\fB, int \fIport\fB, uint64_t \fIts\fB, int \fIopts\fB);\fR .RS `\fIrdb\fR' specifies the remote database object. .RE .RS `\fIhost\fR' specifies the name or the address of the server. If it is `NULL', replication of the database is disabled. .RE .RS `\fIport\fR' specifies the port number. .RE .RS `\fIts\fR' specifies the beginning timestamp in microseconds. .RE .RS `\fIopts\fR' specifies options by bitwise\-or: `RDBROCHKCON' for consistency checking. .RE .RS If successful, the return value is true, else, it is false. .RE .RE .PP The function `tcrdbsetmst2' is used in order to set the replication master of a remote database object with a simple server expression. .PP .RS .br \fBbool tcrdbsetmst2(TCRDB *\fIrdb\fB, const char *\fIexpr\fB, uint64_t \fIts\fB, int \fIopts\fB);\fR .RS `\fIrdb\fR' specifies the remote database object. .RE .RS `\fIexpr\fR' specifies the simple server expression. It is composed of two substrings separated by ":". The former field specifies the name or the address of the server. The latter field specifies the port number. If the latter field is omitted, the default port number is specified. .RE .RS `\fIts\fR' specifies the beginning timestamp in microseconds. .RE .RS `\fIopts\fR' specifies options by bitwise\-or: `RDBROCHKCON' for consistency checking. .RE .RS If successful, the return value is true, else, it is false. .RE .RE .PP The function `tcrdbrnum' is used in order to get the number of records of a remote database object. .PP .RS .br \fBuint64_t tcrdbrnum(TCRDB *\fIrdb\fB);\fR .RS `\fIrdb\fR' specifies the remote database object. .RE .RS The return value is the number of records or 0 if the object does not connect to any database server. .RE .RE .PP The function `tcrdbsize' is used in order to get the size of the database of a remote database object. .PP .RS .br \fBuint64_t tcrdbsize(TCRDB *\fIrdb\fB);\fR .RS `\fIrdb\fR' specifies the remote database object. .RE .RS The return value is the size of the database or 0 if the object does not connect to any database server. .RE .RE .PP The function `tcrdbstat' is used in order to get the status string of the database of a remote database object. .PP .RS .br \fBchar *tcrdbstat(TCRDB *\fIrdb\fB);\fR .RS `\fIrdb\fR' specifies the remote database object. .RE .RS The return value is the status message of the database or `NULL' if the object does not connect to any database server. The message format is TSV. The first field of each line means the parameter name and the second field means the value. .RE .RS Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use. .RE .RE .PP The function `tcrdbmisc' is used in order to call a versatile function for miscellaneous operations of a remote database object. .PP .RS .br \fBTCLIST *tcrdbmisc(TCRDB *\fIrdb\fB, const char *\fIname\fB, int \fIopts\fB, const TCLIST *\fIargs\fB);\fR .RS `\fIrdb\fR' specifies the remote database object. .RE .RS `\fIname\fR' specifies the name of the function. All databases support "put", "out", "get", "putlist", "outlist", and "getlist". "put" is to store a record. It receives a key and a value, and returns an empty list. "out" is to remove a record. It receives a key, and returns an empty list. "get" is to retrieve a record. It receives a key, and returns a list of the values. "putlist" is to store records. It receives keys and values one after the other, and returns an empty list. "outlist" is to remove records. It receives keys, and returns an empty list. "getlist" is to retrieve records. It receives keys, and returns keys and values of corresponding records one after the other. .RE .RS `\fIopts\fR' specifies options by bitwise\-or: `RDBMONOULOG' for omission of the update log. .RE .RS `\fIargs\fR' specifies a list object containing arguments. .RE .RS If successful, the return value is a list object of the result. `NULL' is returned on failure. .RE .RS Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use. .RE .RE .SH TABLE EXTENSION .PP The function `tcrdbtblput' is used in order to store a record into a remote database object. .PP .RS .br \fBbool tcrdbtblput(TCRDB *\fIrdb\fB, const void *\fIpkbuf\fB, int \fIpksiz\fB, TCMAP *\fIcols\fB);\fR .RS `\fIrdb\fR' specifies the remote database object. .RE .RS `\fIpkbuf\fR' specifies the pointer to the region of the primary key. .RE .RS `\fIpksiz\fR' specifies the size of the region of the primary key. .RE .RS `\fIcols\fR' specifies a map object containing columns. .RE .RS If successful, the return value is true, else, it is false. .RE .RS If a record with the same key exists in the database, it is overwritten. .RE .RE .PP The function `tcrdbtblputkeep' is used in order to store a new record into a remote database object. .PP .RS .br \fBbool tcrdbtblputkeep(TCRDB *\fIrdb\fB, const void *\fIpkbuf\fB, int \fIpksiz\fB, TCMAP *\fIcols\fB);\fR .RS `\fIrdb\fR' specifies the remote database object. .RE .RS `\fIpkbuf\fR' specifies the pointer to the region of the primary key. .RE .RS `\fIpksiz\fR' specifies the size of the region of the primary key. .RE .RS `\fIcols\fR' specifies a map object containing columns. .RE .RS If successful, the return value is true, else, it is false. .RE .RS If a record with the same key exists in the database, this function has no effect. .RE .RE .PP The function `tcrdbtblputcat' is used in order to concatenate columns of the existing record in a remote database object. .PP .RS .br \fBbool tcrdbtblputcat(TCRDB *\fIrdb\fB, const void *\fIpkbuf\fB, int \fIpksiz\fB, TCMAP *\fIcols\fB);\fR .RS `\fIrdb\fR' specifies the remote database object. .RE .RS `\fIpkbuf\fR' specifies the pointer to the region of the primary key. .RE .RS `\fIpksiz\fR' specifies the size of the region of the primary key. .RE .RS `\fIcols\fR' specifies a map object containing columns. .RE .RS If successful, the return value is true, else, it is false. .RE .RS If there is no corresponding record, a new record is created. .RE .RE .PP The function `tcrdbtblout' is used in order to remove a record of a remote database object. .PP .RS .br \fBbool tcrdbtblout(TCRDB *\fIrdb\fB, const void *\fIpkbuf\fB, int \fIpksiz\fB);\fR .RS `\fIrdb\fR' specifies the remote database object. .RE .RS `\fIpkbuf\fR' specifies the pointer to the region of the primary key. .RE .RS `\fIpksiz\fR' specifies the size of the region of the primary key. .RE .RS If successful, the return value is true, else, it is false. .RE .RE .PP The function `tcrdbtblget' is used in order to retrieve a record in a remote database object. .PP .RS .br \fBTCMAP *tcrdbtblget(TCRDB *\fIrdb\fB, const void *\fIpkbuf\fB, int \fIpksiz\fB);\fR .RS `\fIrdb\fR' specifies the remote database object. .RE .RS `\fIpkbuf\fR' specifies the pointer to the region of the primary key. .RE .RS `\fIpksiz\fR' specifies the size of the region of the primary key. .RE .RS If successful, the return value is a map object of the columns of the corresponding record. `NULL' is returned if no record corresponds. .RE .RS Because the object of the return value is created with the function `tcmapnew', it should be deleted with the function `tcmapdel' when it is no longer in use. .RE .RE .PP The function `tcrdbtblsetindex' is used in order to set a column index to a remote database object. .PP .RS .br \fBbool tcrdbtblsetindex(TCRDB *\fIrdb\fB, const char *\fIname\fB, int \fItype\fB);\fR .RS `\fIrdb\fR' specifies the remote database object. .RE .RS `\fIname\fR' specifies the name of a column. If the name of an existing index is specified, the index is rebuilt. An empty string means the primary key. .RE .RS `\fItype\fR' specifies the index type: `RDBITLEXICAL' for lexical string, `RDBITDECIMAL' for decimal string, `RDBITTOKEN' for token inverted index, `RDBITQGRAM' for q\-gram inverted index. If it is `RDBITOPT', the index is optimized. If it is `RDBITVOID', the index is removed. If `RDBITKEEP' is added by bitwise\-or and the index exists, this function merely returns failure. .RE .RS If successful, the return value is true, else, it is false. .RE .RE .PP The function `tcrdbtblgenuid' is used in order to generate a unique ID number of a remote database object. .PP .RS .br \fBint64_t tcrdbtblgenuid(TCRDB *\fIrdb\fB);\fR .RS `\fIrdb\fR' specifies the remote database object. .RE .RS The return value is the new unique ID number or \-1 on failure. .RE .RE .PP The function `tcrdbqrynew' is used in order to create a query object. .PP .RS .br \fBRDBQRY *tcrdbqrynew(TCRDB *\fIrdb\fB);\fR .RS `\fIrdb\fR' specifies the remote database object. .RE .RS The return value is the new query object. .RE .RE .PP The function `tcrdbqrydel' is used in order to delete a query object. .PP .RS .br \fBvoid tcrdbqrydel(RDBQRY *\fIqry\fB);\fR .RS `\fIqry\fR' specifies the query object. .RE .RE .PP The function `tcrdbqryaddcond' is used in order to add a narrowing condition to a query object. .PP .RS .br \fBvoid tcrdbqryaddcond(RDBQRY *\fIqry\fB, const char *\fIname\fB, int \fIop\fB, const char *\fIexpr\fB);\fR .RS `\fIqry\fR' specifies the query object. .RE .RS `\fIname\fR' specifies the name of a column. An empty string means the primary key. .RE .RS `\fIop\fR' specifies an operation type: `RDBQCSTREQ' for string which is equal to the expression, `RDBQCSTRINC' for string which is included in the expression, `RDBQCSTRBW' for string which begins with the expression, `RDBQCSTREW' for string which ends with the expression, `RDBQCSTRAND' for string which includes all tokens in the expression, `RDBQCSTROR' for string which includes at least one token in the expression, `RDBQCSTROREQ' for string which is equal to at least one token in the expression, `RDBQCSTRRX' for string which matches regular expressions of the expression, `RDBQCNUMEQ' for number which is equal to the expression, `RDBQCNUMGT' for number which is greater than the expression, `RDBQCNUMGE' for number which is greater than or equal to the expression, `RDBQCNUMLT' for number which is less than the expression, `RDBQCNUMLE' for number which is less than or equal to the expression, `RDBQCNUMBT' for number which is between two tokens of the expression, `RDBQCNUMOREQ' for number which is equal to at least one token in the expression, `RDBQCFTSPH' for full\-text search with the phrase of the expression, `RDBQCFTSAND' for full\-text search with all tokens in the expression, `RDBQCFTSOR' for full\-text search with at least one token in the expression, `RDBQCFTSEX' for full\-text search with the compound expression. All operations can be flagged by bitwise\-or: `RDBQCNEGATE' for negation, `RDBQCNOIDX' for using no index. .RE .RS `\fIexpr\fR' specifies an operand exression. .RE .RE .PP The function `tcrdbqrysetorder' is used in order to set the order of a query object. .PP .RS .br \fBvoid tcrdbqrysetorder(RDBQRY *\fIqry\fB, const char *\fIname\fB, int \fItype\fB);\fR .RS `\fIqry\fR' specifies the query object. .RE .RS `\fIname\fR' specifies the name of a column. An empty string means the primary key. .RE .RS `\fItype\fR' specifies the order type: `RDBQOSTRASC' for string ascending, `RDBQOSTRDESC' for string descending, `RDBQONUMASC' for number ascending, `RDBQONUMDESC' for number descending. .RE .RE .PP The function `tcrdbqrysetlimit' is used in order to set the limit number of records of the result of a query object. .PP .RS .br \fBvoid tcrdbqrysetlimit(RDBQRY *\fIqry\fB, int \fImax\fB, int \fIskip\fB);\fR .RS `\fIqry\fR' specifies the query object. .RE .RS `\fImax\fR' specifies the maximum number of records of the result. If it is negative, no limit is specified. .RE .RS `\fIskip\fR' specifies the number of skipped records of the result. If it is not more than 0, no record is skipped. .RE .RE .PP The function `tcrdbqrysearch' is used in order to execute the search of a query object. .PP .RS .br \fBTCLIST *tcrdbqrysearch(RDBQRY *\fIqry\fB);\fR .RS `\fIqry\fR' specifies the query object. .RE .RS The return value is a list object of the primary keys of the corresponding records. This function does never fail. It returns an empty list even if no record corresponds. .RE .RS Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use. .RE .RE .PP The function `tcrdbqrysearchout' is used in order to remove each record corresponding to a query object. .PP .RS .br \fBbool tcrdbqrysearchout(RDBQRY *\fIqry\fB);\fR .RS `\fIqry\fR' specifies the query object of the database. .RE .RS If successful, the return value is true, else, it is false. .RE .RE .PP The function `tcrdbqrysearchget' is used in order to get records corresponding to the search of a query object. .PP .RS .br \fBTCLIST *tcrdbqrysearchget(RDBQRY *\fIqry\fB);\fR .RS `\fIqry\fR' specifies the query object. .RE .RS The return value is a list object of zero separated columns of the corresponding records. .RE .RS This function does never fail. It returns an empty list even if no record corresponds. Each element of the list can be treated with the function `tcrdbqryrescols'. Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use. .RE .RE .PP The function `tcrdbqryrescols' is used in order to get columns of a record in a search result. .PP .RS .br \fBTCMAP *tcrdbqryrescols(TCLIST *\fIres\fB, int \fIindex\fB);\fR .RS `\fIres\fR' specifies a list of zero separated columns of the search result. .RE .RS `\fIindex\fR' the index of a element of the search result. .RE .RS The return value is a map object containing columns. .RE .RS Because the object of the return value is created with the function `tcmapnew', it should be deleted with the function `tcmapdel' when it is no longer in use. .RE .RE .PP The function `tcrdbqrysearchcount' is used in order to get the count of corresponding records of a query object. .PP .RS .br \fBint tcrdbqrysearchcount(RDBQRY *\fIqry\fB);\fR .RS `\fIqry\fR' specifies the query object. .RE .RS The return value is the count of corresponding records or 0 on failure. .RE .RE .PP The function `tcrdbqryhint' is used in order to get the hint string of a query object. .PP .RS .br \fBconst char *tcrdbqryhint(RDBQRY *\fIqry\fB);\fR .RS `\fIqry\fR' specifies the query object. .RE .RS The return value is the hint string. .RE .RS This function should be called after the query execution by `tcrdbqrysearch' and so on. The region of the return value is overwritten when this function is called again. .RE .RE .PP The function `tcrdbmetasearch' is used in order to retrieve records with multiple query objects and get the set of the result. .PP .RS .br \fBTCLIST *tcrdbmetasearch(RDBQRY **\fIqrys\fB, int \fInum\fB, int \fItype\fB);\fR .RS `\fIqrys\fR' specifies an array of the query objects. .RE .RS `\fInum\fR' specifies the number of elements of the array. .RE .RS `\fItype\fR' specifies a set operation type: `RDBMSUNION' for the union set, `RDBMSISECT' for the intersection set, `RDBMSDIFF' for the difference set. .RE .RS The return value is a list object of the primary keys of the corresponding records. This function does never fail. It returns an empty list even if no record corresponds. .RE .RS If the first query object has the order setting, the result array is sorted by the order. Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use. .RE .RE .PP The function `tcrdbparasearch' is used in order to search records for multiple servers in parallel. .PP .RS .br \fBTCLIST *tcrdbparasearch(RDBQRY **\fIqrys\fB, int \fInum\fB);\fR .RS `\fIqrys\fR' specifies an array of the query objects. .RE .RS `\fInum\fR' specifies the number of elements of the array. .RE .RS The return value is a list object of zero separated columns of the corresponding records. .RE .RS This function does never fail. It returns an empty list even if no record corresponds. Each element of the list can be treated with the function `tcrdbqryrescols'. Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use. .RE .RE .SH SEE ALSO .PP .BR ttserver (1), .BR tcrtest (1), .BR tcrmttest (1), .BR tcrmgr (1), .BR ttutil (3) tokyotyrant-1.1.40/man/tcrmgr.1000066400000000000000000000132341133351147200163420ustar00rootroot00000000000000.TH "TCRMGR" 1 "2010-01-20" "Man Page" "Tokyo Tyrant" .SH NAME tcrmgr \- the command line utility of the remote database API .SH DESCRIPTION The command `\fBtcrmgr\fR' is a utility for test and debugging of the remote database API and its applications. `\fIhost\fR' specifies the host name of the server. `\fIkey\fR' specifies the key of a record. `\fIvalue\fR' specifies the value of a record. `\fIparams\fR' specifies the tuning parameters. `\fIdpath\fR' specifies the destination file. `\fIfunc\fR specifies the name of the function. `\fIarg\fR' specifies the arguments of the function. `\fIfile\fR' specifies the input file. `\fIupath\fR' specifies the update log directory. `\fImhost\fR' specifies the host name of the replication master. `\fIurl\fR' specifies the target URL. .PP .RS .br \fBtcrmgr inform \fR[\fB\-port \fInum\fB\fR]\fB \fR[\fB\-st\fR]\fB \fIhost\fB\fR .RS Print miscellaneous information to the standard output. .RE .br \fBtcrmgr put \fR[\fB\-port \fInum\fB\fR]\fB \fR[\fB\-sx\fR]\fB \fR[\fB\-sep \fIchr\fB\fR]\fB \fR[\fB\-dk\fR|\fB\-dc\fR|\fB\-dai\fR|\fB\-dad\fR]\fB \fIhost\fB \fIkey\fB \fIvalue\fB\fR .RS Store a record. .RE .br \fBtcrmgr out \fR[\fB\-port \fInum\fB\fR]\fB \fR[\fB\-sx\fR]\fB \fR[\fB\-sep \fIchr\fB\fR]\fB \fIhost\fB \fIkey\fB\fR .RS Remove a record. .RE .br \fBtcrmgr get \fR[\fB\-port \fInum\fB\fR]\fB \fR[\fB\-sx\fR]\fB \fR[\fB\-sep \fIchr\fB\fR]\fB \fR[\fB\-px\fR]\fB \fR[\fB\-pz\fR]\fB \fIhost\fB \fIkey\fB\fR .RS Print the value of a record. .RE .br \fBtcrmgr mget \fR[\fB\-port \fInum\fB\fR]\fB \fR[\fB\-sx\fR]\fB \fR[\fB\-sep \fIchr\fB\fR]\fB \fR[\fB\-px\fR]\fB \fIhost\fB \fR[\fB\fIkey\fB...\fR]\fB\fR .RS Print keys and values of multiple records. .RE .br \fBtcrmgr list \fR[\fB\-port \fInum\fB\fR]\fB \fR[\fB\-sep \fIchr\fB\fR]\fB \fR[\fB\-m \fInum\fB\fR]\fB \fR[\fB\-pv\fR]\fB \fR[\fB\-px\fR]\fB \fR[\fB\-fm \fIstr\fB\fR]\fB \fIhost\fB\fR .RS Print keys of all records, separated by line feeds. .RE .br \fBtcrmgr ext \fR[\fB\-port \fInum\fB\fR]\fB \fR[\fB\-xlr\fR|\fB\-xlg\fR]\fB \fR[\fB\-sx\fR]\fB \fR[\fB\-sep \fIchr\fB\fR]\fB \fR[\fB\-px\fR]\fB \fIhost\fB \fIfunc\fB \fR[\fB\fIkey\fB \fR[\fB\fIvalue\fB\fR]\fB\fR]\fB\fR .RS Call a script language extension function. .RE .br \fBtcrmgr sync \fR[\fB\-port \fInum\fB\fR]\fB \fIhost\fB\fR .RS Synchronize updated contents with the database file. .RE .br \fBtcrmgr optimize \fR[\fB\-port \fInum\fB\fR]\fB \fIhost\fB \fR[\fB\fIparams\fB\fR]\fB\fR .RS Optimize the database file. .RE .br \fBtcrmgr vanish \fR[\fB\-port \fInum\fB\fR]\fB \fIhost\fB\fR .RS Remove all records. .RE .br \fBtcrmgr copy \fR[\fB\-port \fInum\fB\fR]\fB \fIhost\fB \fIdpath\fB\fR .RS Copy the database file. .RE .br \fBtcrmgr misc \fR[\fB\-port \fInum\fB\fR]\fB \fR[\fB\-mnu\fR]\fB \fR[\fB\-sx\fR]\fB \fR[\fB\-sep \fIchr\fB\fR]\fB \fR[\fB\-px\fR]\fB \fIhost\fB \fIfunc\fB \fR[\fB\fIarg\fB...\fR]\fB\fR .RS Call a versatile function for miscellaneous operations. .RE .br \fBtcrmgr importtsv \fR[\fB\-port \fInum\fB\fR]\fB \fR[\fB\-nr\fR]\fB \fR[\fB\-sc\fR]\fB \fIhost\fB \fR[\fB\fIfile\fB\fR]\fB\fR .RS Store records of TSV in each line of a file. .RE .br \fBtcrmgr restore \fR[\fB\-port \fInum\fB\fR]\fB \fR[\fB\-ts \fInum\fB\fR]\fB \fR[\fB\-rcc\fR]\fB \fIhost\fB \fIupath\fB\fR .RS Restore the database with update log. .RE .br \fBtcrmgr setmst \fR[\fB\-port \fInum\fB\fR]\fB \fR[\fB\-mport \fInum\fB\fR]\fB \fR[\fB\-ts \fInum\fB\fR]\fB \fR[\fB\-rcc\fR]\fB \fIhost\fB \fR[\fB\fImhost\fB\fR]\fB\fR .RS Set the replication master. .RE .br \fBtcrmgr repl \fR[\fB\-port \fInum\fB\fR]\fB \fR[\fB\-ts \fInum\fB\fR]\fB \fR[\fB\-sid \fInum\fB\fR]\fB \fR[\fB\-ph\fR]\fB \fIhost\fB\fR .RS Replicate the update log. .RE .br \fBtcrmgr http \fR[\fB\-ah \fIname\fB \fIvalue\fB\fR]\fB \fR[\fB\-ih\fR]\fB \fIurl\fB\fR .RS Fetch the resource of a URL by HTTP. .RE .br \fBtcrmgr version\fR .RS Print the version information of Tokyo Tyrant. .RE .RE .PP Options feature the following. .PP .RS \fB\-port \fInum\fR\fR : specify the port number. .br \fB\-st\fR : print miscellaneous status data. .br \fB\-sx\fR : input data is evaluated as a hexadecimal data string. .br \fB\-sep \fIchr\fR\fR : specify the separator of the input data. .br \fB\-dk\fR : use the function `tcrdbputkeep' instead of `tcrdbput'. .br \fB\-dc\fR : use the function `tcrdbputcat' instead of `tcrdbput'. .br \fB\-dai\fR : use the function `tcrdbaddint' instead of `tcrdbput'. .br \fB\-dad\fR : use the function `tcrdbadddouble' instead of `tcrdbput'. .br \fB\-px\fR : output data is converted into a hexadecimal data string. .br \fB\-pz\fR : do not append line feed at the end of the output. .br \fB\-m \fInum\fR\fR : specify the maximum number of the output. .br \fB\-pv\fR : print values of records also. .br \fB\-fm \fIstr\fR\fR : specify the prefix of keys. .br \fB\-xlr\fR : perform record locking. .br \fB\-xlg\fR : perform global locking. .br \fB\-mnu\fR : omit the update log. .br \fB\-nr\fR : use the function `tcrdbputnr' instead of `tcrdbput'. .br \fB\-sc\fR : normalize keys as lower cases. .br \fB\-mport \fInum\fR\fR : specify the port number of the replication master. .br \fB\-ts \fInum\fR\fR : specify the beginning time stamp. .br \fB\-rcc\fR : check consistency of replication. .br \fB\-sid \fInum\fR\fR : specify the self server ID. .br \fB\-ph\fR : print human\-readable data. .br \fB\-ah \fIname\fR \fIvalue\fR\fR : add a request header. .br \fB\-ih\fR : output response headers also. .br .RE .PP If the port number is not more than 0, UNIX domain socket is used and the path of the socket file is specified by the host parameter. This command returns 0 on success, another on failure. .SH SEE ALSO .PP .BR ttserver (1), .BR ttultest (1), .BR ttulmgr (1), .BR tcrtest (1), .BR tcrmttest (1), .BR ttutil (3), .BR tcrdb (3) tokyotyrant-1.1.40/man/tcrmttest.1000066400000000000000000000033251133351147200170750ustar00rootroot00000000000000.TH "TCRMTTEST" 1 "2010-01-20" "Man Page" "Tokyo Tyrant" .SH NAME tcrmttest \- test cases of the remote database API .SH DESCRIPTION .PP The command `\fBtcrmttest\fR' is a utility for facility test under multi\-thread situation. This command is used in the following format. `\fIhost\fR' specifies the host name of the server. `\fIrnum\fR' specifies the number of iterations. .PP .RS .br \fBtcrmttest write \fR[\fB\-port \fInum\fB\fR]\fB \fR[\fB\-tnum \fInum\fB\fR]\fB \fR[\fB\-nr\fR]\fB \fR[\fB\-rnd\fR]\fB \fR[\fB\-ext \fIname\fB\fR]\fB \fIhost\fB \fIrnum\fB\fR .RS Store records with keys of 8 bytes. They change as `00000001', `00000002'... .RE .br \fBtcrmttest read \fR[\fB\-port \fInum\fB\fR]\fB \fR[\fB\-tnum \fInum\fB\fR]\fB \fR[\fB\-mul \fInum\fB\fR]\fB \fIhost\fB\fR .RS Retrieve all records of the database above. .RE .br \fBtcrmttest remove \fR[\fB\-port \fInum\fB\fR]\fB \fR[\fB\-tnum \fInum\fB\fR]\fB \fIhost\fB\fR .RS Remove all records of the database above. .RE .RE .PP Options feature the following. .PP .RS \fB\-port \fInum\fR\fR : specify the port number. .br \fB\-tnum \fInum\fR\fR : specify the number of running threads. .br \fB\-nr\fR : use the function `tcrdbputnr' instead of `tcrdbput'. .br \fB\-rnd\fR : select keys at random. .br \fB\-ext \fIname\fR\fR : call a script language extension function. .br \fB\-mul \fInum\fR\fR : specify the number of records for the mget command. .br .RE .PP If the port number is not more than 0, UNIX domain socket is used and the path of the socket file is specified by the host parameter. This command returns 0 on success, another on failure. .SH SEE ALSO .PP .BR ttserver (1), .BR ttultest (1), .BR ttulmgr (1), .BR tcrtest (1), .BR tcrmgr (1), .BR ttutil (3), .BR tcrdb (3) tokyotyrant-1.1.40/man/tcrtest.1000066400000000000000000000062341133351147200165360ustar00rootroot00000000000000.TH "TCRTEST" 1 "2010-01-20" "Man Page" "Tokyo Tyrant" .SH NAME tcrtest \- test cases of the remote database API .SH DESCRIPTION .PP The command `\fBtcrtest\fR' is a utility for facility test and performance test. This command is used in the following format. `\fIhost\fR' specifies the host name of the server. `\fIrnum\fR' specifies the number of iterations. .PP .RS .br \fBtcrtest write \fR[\fB\-port \fInum\fB\fR]\fB \fR[\fB\-cnum \fInum\fB\fR]\fB \fR[\fB\-tout \fInum\fB\fR]\fB \fR[\fB\-nr\fR]\fB \fR[\fB\-rnd\fR]\fB \fIhost\fB \fIrnum\fB\fR .RS Store records with keys of 8 bytes. They change as `00000001', `00000002'... .RE .br \fBtcrtest read \fR[\fB\-port \fInum\fB\fR]\fB \fR[\fB\-cnum \fInum\fB\fR]\fB \fR[\fB\-tout \fInum\fB\fR]\fB \fR[\fB\-mul \fInum\fB\fR]\fB \fR[\fB\-rnd\fR]\fB \fIhost\fB\fR .RS Retrieve all records of the database above. .RE .br \fBtcrtest remove \fR[\fB\-port \fInum\fB\fR]\fB \fR[\fB\-cnum \fInum\fB\fR]\fB \fR[\fB\-tout \fInum\fB\fR]\fB \fR[\fB\-rnd\fR]\fB \fIhost\fB\fR .RS Remove all records of the database above. .RE .br \fBtcrtest rcat \fR[\fB\-port \fInum\fB\fR]\fB \fR[\fB\-cnum \fInum\fB\fR]\fB \fR[\fB\-tout \fInum\fB\fR]\fB \fR[\fB\-shl \fInum\fB\fR]\fB \fR[\fB\-dai\fR|\fB\-dad\fR]\fB \fR[\fB\-ext \fIname\fB\fR]\fB \fR[\fB\-xlr\fR|\fB\-xlg\fR]\fB \fIhost\fB \fIrnum\fB\fR .RS Store records with partway duplicated keys using concatenate mode. .RE .br \fBtcrtest misc \fR[\fB\-port \fInum\fB\fR]\fB \fR[\fB\-cnum \fInum\fB\fR]\fB \fR[\fB\-tout \fInum\fB\fR]\fB \fIhost\fB \fIrnum\fB\fR .RS Perform miscellaneous test of various operations. .RE .br \fBtcrtest wicked \fR[\fB\-port \fInum\fB\fR]\fB \fR[\fB\-cnum \fInum\fB\fR]\fB \fR[\fB\-tout \fInum\fB\fR]\fB \fIhost\fB \fIrnum\fB\fR .RS Perform updating operations of list and map selected at random. .RE .br \fBtcrtest table \fR[\fB\-port \fInum\fB\fR]\fB \fR[\fB\-cnum \fInum\fB\fR]\fB \fR[\fB\-tout \fInum\fB\fR]\fB \fR[\fB\-exp \fInum\fB\fR]\fB \fIhost\fB \fIrnum\fB\fR .RS Perform miscellaneous test of the table extension. .RE .RE .PP Options feature the following. .PP .RS \fB\-port \fInum\fR\fR : specify the port number. .br \fB\-cnum \fInum\fR\fR : specify the number of connections. .br \fB\-tout \fInum\fR\fR : specify the timeout of each session in seconds. .br \fB\-nr\fR : use the function `tcrdbputnr' instead of `tcrdbput'. .br \fB\-rnd\fR : select keys at random. .br \fB\-mul \fInum\fR\fR : specify the number of records for the mget command. .br \fB\-shl \fInum\fR\fR : use `tcrdbputshl' and specify the width. .br \fB\-dai\fR : use `tcrdbaddint' instead of `tcrdbputcat'. .br \fB\-dad\fR : use `tcrdbadddouble' instead of `tcrdbputcat'. .br \fB\-ext \fIname\fR\fR : call a script language extension function. .br \fB\-xlr\fR : perform record locking. .br \fB\-xlg\fR : perform global locking. .br \fB\-exp \fInum\fR\fR : specify the lifetime of expiration test. .br .RE .PP If the port number is not more than 0, UNIX domain socket is used and the path of the socket file is specified by the host parameter. This command returns 0 on success, another on failure. .SH SEE ALSO .PP .BR ttserver (1), .BR ttultest (1), .BR ttulmgr (1), .BR tcrmttest (1), .BR tcrmgr (1), .BR ttutil (3), .BR tcrdb (3) tokyotyrant-1.1.40/man/tculog.3000066400000000000000000000003151133351147200163370ustar00rootroot00000000000000.TH "TCULOG" 3 "2010-01-20" "Man Page" "Tokyo Tyrant" .SH NAME tculog \- the update log API .SH DESCRIPTION .PP hey. .SH SEE ALSO .PP .BR ttserver (1), .BR ttultest (1), .BR ttulmgr (1), .BR ttutil (3) tokyotyrant-1.1.40/man/ttservctl.8000066400000000000000000000014541133351147200171060ustar00rootroot00000000000000.TH "TTSERVCTL" 1 "2010-01-20" "Man Page" "Tokyo Tyrant" .SH NAME ttserctl \- the startup script of the server of Tokyo Tyrant .SH DESCRIPTION .PP The command `\fBttservctl\fR' is the startup script of the server. It can be called by the RC script of the bootstrap process of the operating system. This command is used in the following format. .PP .RS .br \fBttservctl start\fR .RS Startup the server. .RE .br \fBttservctl stop\fR .RS Stop the server. .RE .br \fBttservctl restart\fR .RS Restart the server. .RE .br \fBttservctl hup\fR .RS Send HUP signal to the server for log rotation. .RE .RE .PP The database is placed as "/var/ttserver/casket.tch". The log and related files are also placed in "/var/ttserver". This command returns 0 on success, another on failure. .SH SEE ALSO .PP .BR ttserver (1) tokyotyrant-1.1.40/man/ttserver.1000066400000000000000000000147231133351147200167260ustar00rootroot00000000000000.TH "TTSERVER" 1 "2010-01-20" "Man Page" "Tokyo Tyrant" .SH NAME ttserver \- the server of Tokyo Tyrant .SH DESCRIPTION .PP The command `\fBttserver\fR' runs the server managing a database instance. Because the database is treated by the abstract API of Tokyo Cabinet, you can choose the scheme on start\-up of the server. Supported schema are on\-memory hash database, on\-memory tree database, hash database, and B+ tree database. This command is used in the following format. `\fIdbname\fR' specifies the database name. If it is omitted, on\-memory hash database is specified. .PP .RS .br \fBttserver \fR[\fB\-host \fIname\fB\fR]\fB \fR[\fB\-port \fInum\fB\fR]\fB \fR[\fB\-thnum \fInum\fB\fR]\fB \fR[\fB\-tout \fInum\fB\fR]\fB \fR[\fB\-dmn\fR]\fB \fR[\fB\-pid \fIpath\fB\fR]\fB \fR[\fB\-kl\fR]\fB \fR[\fB\-log \fIpath\fB\fR]\fB \fR[\fB\-ld\fR|\fB\-le\fR]\fB \fR[\fB\-ulog \fIpath\fB\fR]\fB \fR[\fB\-ulim \fInum\fB\fR]\fB \fR[\fB\-uas\fR]\fB \fR[\fB\-sid \fInum\fB\fR]\fB \fR[\fB\-mhost \fIname\fB\fR]\fB \fR[\fB\-mport \fInum\fB\fR]\fB \fR[\fB\-rts \fIpath\fB\fR]\fB \fR[\fB\-rcc\fR]\fB \fR[\fB\-skel \fIname\fB\fR]\fB \fR[\fB\-mul \fInum\fB\fR]\fB \fR[\fB\-ext \fIpath\fB\fR]\fB \fR[\fB\-extpc \fIname\fB \fIperiod\fB\fR]\fB \fR[\fB\-mask \fIexpr\fB\fR]\fB \fR[\fB\-unmask \fIexpr\fB\fR]\fB \fR[\fB\fIdbname\fB\fR]\fB\fR .RE .PP Options feature the following. .PP .RS \fB\-host \fIname\fR\fR : specify the host name or the address of the server. By default, every network address is bound. .br \fB\-port \fInum\fR\fR : specify the port number. By default, it is 1978. .br \fB\-thnum \fInum\fR\fR : specify the number of worker threads. By default, it is 8. .br \fB\-tout \fInum\fR\fR : specify the timeout of each session in seconds. By default, no timeout is specified. .br \fB\-dmn\fR : work as a daemon process. .br \fB\-pid \fIpath\fR\fR : output the process ID into the file. .br \fB\-kl\fR : kill the existing process if the process ID file is detected. .br \fB\-log \fIpath\fR\fR : output log messages into the file. .br \fB\-ld\fR : log debug messages also. .br \fB\-le\fR : log error messages only. .br \fB\-ulog \fIpath\fR\fR : specify the update log directory. .br \fB\-ulim \fInum\fR\fR : specify the limit size of each update log file. .br \fB\-uas\fR : use asynchronous I/O for the update log. .br \fB\-sid \fInum\fR\fR : specify the server ID. .br \fB\-mhost \fIname\fR\fR : specify the host name of the replication master server. .br \fB\-mport \fInum\fR\fR : specify the port number of the replication master server. .br \fB\-rts \fIpath\fR\fR : specify the replication time stamp file. .br \fB\-rcc\fR : check consistency of replication. .br \fB\-skel \fIname\fR\fR : specify the name of the skeleton database library. .br \fB\-mul \fInum\fR\fR : specify the division number of the multiple database mechanism. .br \fB\-ext \fIpath\fR\fR : specify the script language extension file. .br \fB\-extpc \fIname\fR \fIperiod\fR\fR : specify the function name and the calling period of a periodic command. .br \fB\-mask \fIexpr\fR\fR : specify the names of forbidden commands. .br \fB\-unmask \fIexpr\fR\fR : specify the names of allowed commands. .br .RE .PP To terminate the server normally, send SIGINT or SIGTERM to the process. It is okay to press Ctrl\-C on the controlling terminal. To restart the server, send SIGHUP to the process. If the port number is not more than 0, UNIX domain socket is used and the path of the socket file is specified by the host parameter. This command returns 0 on success, another on failure. .PP The naming convention of the database is specified by the abstract API of Tokyo Cabinet. If the name is "*", the database will be an on\-memory hash database. If it is "+", the database will be an on\-memory tree database. If its suffix is ".tch", the database will be a hash database. If its suffix is ".tcb", the database will be a B+ tree database. If its suffix is ".tcf", the database will be a fixed\-length database. If its suffix is ".tct", the database will be a table database. Otherwise, this function fails. Tuning parameters can trail the name, separated by "#". Each parameter is composed of the name and the value, separated by "=". On\-memory hash database supports "bnum", "capnum", and "capsiz". On\-memory tree database supports "capnum" and "capsiz". Hash database supports "mode", "bnum", "apow", "fpow", "opts", "rcnum", and "xmsiz". B+ tree database supports "mode", "lmemb", "nmemb", "bnum", "apow", "fpow", "opts", "lcnum", "ncnum", and "xmsiz". Fixed\-length database supports "mode", "width", and "limsiz". Table database supports "mode", "bnum", "apow", "fpow", "opts", "rcnum", "lcnum", "ncnum", "xmsiz", and "idx". The tuning parameter "capnum" specifies the capacity number of records. "capsiz" specifies the capacity size of using memory. Records spilled the capacity are removed by the storing order. "mode" can contain "w" of writer, "r" of reader, "c" of creating, "t" of truncating, "e" of no locking, and "f" of non\-blocking lock. The default mode is relevant to "wc". "opts" can contains "l" of large option, "d" of Deflate option, "b" of BZIP2 option, and "t" of TCBS option. "idx" specifies the column name of an index and its type separated by ":". For example, "casket.tch#bnum=1000000#opts=ld" means that the name of the database file is "casket.tch", and the bucket number is 1000000, and the options are large and Deflate. .PP The command mask expression is a list of command names separated by ",". For example, "out,vanish,copy" means a set of "out", "vanish", and "copy". Commands of the memcached compatible protocol and the HTTP compatible protocol are also forbidden or allowed, related by the mask of each original command. Moreover, there are meta expressions. "all" means all commands. "allorg" means all commands of the original binary protocol. "allmc" means all commands of the memcached compatible protocol. "allhttp" means all commands of the HTTP compatible protocol. "allread" is the abbreviation of `get', `mget', `vsiz', `iterinit', `iternext', `fwmkeys', `rnum', `size', and `stat'. "allwrite" is the abbreviation of `put', `putkeep', `putcat', `putshl', `putnr', `out', `addint', `adddouble', `vanish', and `misc'. "allmanage" is the abbreviation of `sync', `optimize', `copy', `restore', and `setmst'. "repl" means replication as master. "slave" means replication as slave. .SH SEE ALSO .PP .BR ttultest (1), .BR ttulmgr (1), .BR tcrtest (1), .BR tcrmttest (1), .BR tcrmgr (1), .BR ttutil (3), .BR ttulog (3), .BR tcrdb (3), .BR ttservctl (8) tokyotyrant-1.1.40/man/ttulmgr.1000066400000000000000000000020061133351147200165350ustar00rootroot00000000000000.TH "TTULMGR" 1 "2010-01-20" "Man Page" "Tokyo Tyrant" .SH NAME ttulmgr \- the command line utility of the update log API .SH DESCRIPTION .PP The command `\fBttulmgr\fR' is the utility to export and import the update log. It is useful to filter the update log with such text utilities as `\fBgrep\fR' and `\fBsed\fR'. This command is used in the following format. `\fIupath\fR' specifies the update log directory. .PP .RS .br \fBttulmgr export \fR[\fB\-ts \fInum\fB\fR]\fB \fR[\fB\-sid \fInum\fB\fR]\fB \fIupath\fB\fR .RS Export the update log as TSV text data to the standard output. .RE .br \fBttulmgr import \fIupath\fB\fR .RS Import TSV text data from the standard input to the update log. .RE .RE .PP Options feature the following. .PP .RS \fB\-ts\fR \fInum\fR : specify the beginning time stamp. .br \fB\-sid\fR \fInum\fR : specify the self server ID. .br .RE .PP This command returns 0 on success, another on failure. .SH SEE ALSO .PP .BR ttserver (1), .BR ttultest (1), .BR ttutil (3), .BR tculog (3), .BR tcrdb (3) tokyotyrant-1.1.40/man/ttultest.1000066400000000000000000000003541133351147200167330ustar00rootroot00000000000000.TH "TTULTEST" 1 "2010-01-20" "Man Page" "Tokyo Tyrant" .SH NAME ttultest \- test cases of the update log API .SH DESCRIPTION .PP hey. .SH SEE ALSO .PP .BR ttserver (1), .BR ttulmgr (1), .BR ttutil (3), .BR tculog (3), .BR tcrdb (3) tokyotyrant-1.1.40/man/ttutil.3000066400000000000000000000002771133351147200163760ustar00rootroot00000000000000.TH "TTUTIL" 3 "2010-01-20" "Man Page" "Tokyo Tyrant" .SH NAME ttutil \- the utility API .SH DESCRIPTION .PP now printing. .SH SEE ALSO .PP .BR ttserver (1), .BR tcrmgr (1), .BR tcrdb (3) tokyotyrant-1.1.40/myconf.c000066400000000000000000000110541133351147200156440ustar00rootroot00000000000000/************************************************************************************************* * System-dependent configurations of Tokyo Tyrant * Copyright (C) 2006-2010 Mikio Hirabayashi * This file is part of Tokyo Tyrant. * Tokyo Tyrant is free software; you can redistribute it and/or modify it under the terms of * the GNU Lesser General Public License as published by the Free Software Foundation; either * version 2.1 of the License or any later version. Tokyo Tyrant 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 Lesser General Public * License for more details. * You should have received a copy of the GNU Lesser General Public License along with Tokyo * Tyrant; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA. *************************************************************************************************/ #include "myconf.h" /************************************************************************************************* * common settings *************************************************************************************************/ int _tt_dummyfunc(void){ return 0; } int _tt_dummyfuncv(int a, ...){ return 0; } /************************************************************************************************* * epoll emulation *************************************************************************************************/ #if defined(TTUSEKQUEUE) #include int _tt_epoll_create(int size){ return kqueue(); } int _tt_epoll_ctl(int epfd, int op, int fd, struct epoll_event *event){ if(op == EPOLL_CTL_ADD || op == EPOLL_CTL_MOD){ struct kevent kev; int kfilt = 0; if(event->events & EPOLLIN){ kfilt |= EVFILT_READ; } else { fprintf(stderr, "the epoll emulation supports EPOLLIN only\n"); return -1; } if(event->events & EPOLLOUT){ fprintf(stderr, "the epoll emulation supports EPOLLIN only\n"); return -1; } int kflags = EV_ADD; if(event->events & EPOLLONESHOT) kflags |= EV_ONESHOT; EV_SET(&kev, fd, kfilt, kflags, 0, 0, NULL); return kevent(epfd, &kev, 1, NULL, 0, NULL) != -1 ? 0 : -1; } else if(op == EPOLL_CTL_DEL){ struct kevent kev; int kfilt = EVFILT_READ; EV_SET(&kev, fd, kfilt, EV_DELETE, 0, 0, NULL); return kevent(epfd, &kev, 1, NULL, 0, NULL) != -1 || errno == ENOENT ? 0 : -1; } return -1; } int _tt_epoll_reassoc(int epfd, int fd){ return 0; } int _tt_epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout){ div_t td = div(timeout, 1000); struct timespec ts; ts.tv_sec = td.quot; ts.tv_nsec = td.rem * 1000000; struct kevent kevs[maxevents]; int num = kevent(epfd, NULL, 0, kevs, maxevents, &ts); if(num == -1) return -1; for(int i = 0; i < num; i++){ events[i].data.fd = kevs[i].ident; } return num; } #elif defined(TTUSEEVPORTS) #include int _tt_epoll_create(int size){ int port = port_create(); if(port < 0) return -1; return port; } int _tt_epoll_ctl(int epfd, int op, int fd, struct epoll_event *event){ if(op == EPOLL_CTL_ADD || op == EPOLL_CTL_MOD){ if(event->events & EPOLLIN){ int result = port_associate(epfd, PORT_SOURCE_FD, fd, POLLIN, event); if(result == -1) return -1; return 0; } else { return -1; } } else if(op == EPOLL_CTL_DEL){ int result = port_dissociate(epfd, PORT_SOURCE_FD, fd); if(result == -1) return -1; return 0; } return -1; } int _tt_epoll_reassoc(int epfd, int fd){ struct epoll_event ev; ev.events = EPOLLIN; ev.data.fd = fd; if(epoll_ctl(epfd, EPOLL_CTL_MOD, fd, &ev) != 0) return -1; return 0; } int _tt_epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout){ div_t td = div(timeout, 1000); struct timespec ts; ts.tv_sec = td.quot; ts.tv_nsec = td.rem * 1000000; port_event_t list[maxevents]; unsigned int actevents = maxevents; if(port_getn(epfd, list, 0, &actevents, &ts) == -1) return -1; if(actevents == 0) actevents = 1; if(actevents > maxevents) actevents = maxevents; if(port_getn(epfd, list, maxevents, &actevents, &ts) == -1){ if(errno == EINTR){ return 0; } else if(errno != ETIME){ return -1; } } for(int i = 0; i < actevents; i++){ events[i].data.fd = list[i].portev_object; } return actevents; } #endif // END OF FILE tokyotyrant-1.1.40/myconf.h000066400000000000000000000242671133351147200156630ustar00rootroot00000000000000/************************************************************************************************* * System-dependent configurations of Tokyo Tyrant * Copyright (C) 2006-2010 Mikio Hirabayashi * This file is part of Tokyo Tyrant. * Tokyo Tyrant is free software; you can redistribute it and/or modify it under the terms of * the GNU Lesser General Public License as published by the Free Software Foundation; either * version 2.1 of the License or any later version. Tokyo Tyrant 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 Lesser General Public * License for more details. * You should have received a copy of the GNU Lesser General Public License along with Tokyo * Tyrant; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA. *************************************************************************************************/ #ifndef _MYCONF_H // duplication check #define _MYCONF_H /************************************************************************************************* * system discrimination *************************************************************************************************/ #if defined(__linux__) #define _SYS_LINUX_ #define TTSYSNAME "Linux" #elif defined(__FreeBSD__) #define _SYS_FREEBSD_ #define TTSYSNAME "FreeBSD" #elif defined(__NetBSD__) #define _SYS_NETBSD_ #define TTSYSNAME "NetBSD" #elif defined(__OpenBSD__) #define _SYS_OPENBSD_ #define TTSYSNAME "OpenBSD" #elif defined(__sun__) || defined(__sun) #define _SYS_SUNOS_ #define TTSYSNAME "SunOS" #elif defined(__hpux) #define _SYS_HPUX_ #define TTSYSNAME "HP-UX" #elif defined(__osf) #define _SYS_TRU64_ #define TTSYSNAME "Tru64" #elif defined(_AIX) #define _SYS_AIX_ #define TTSYSNAME "AIX" #elif defined(__APPLE__) && defined(__MACH__) #define _SYS_MACOSX_ #define TTSYSNAME "Mac OS X" #elif defined(_MSC_VER) #define _SYS_MSVC_ #define TTSYSNAME "Windows (VC++)" #elif defined(_WIN32) #define _SYS_MINGW_ #define TTSYSNAME "Windows (MinGW)" #elif defined(__CYGWIN__) #define _SYS_CYGWIN_ #define TTSYSNAME "Windows (Cygwin)" #else #define _SYS_GENERIC_ #define TTSYSNAME "Generic" #endif #if !defined(_SYS_LINUX_) && !defined(_SYS_FREEBSD_) && !defined(_SYS_MACOSX_) && \ !defined(_SYS_SUNOS_) #error ======================================= #error Your platform is not supported. Sorry. #error ======================================= #endif /************************************************************************************************* * common settings *************************************************************************************************/ #if defined(NDEBUG) #define TTDODEBUG(TT_expr) \ do { \ } while(false) #else #define TTDODEBUG(TT_expr) \ do { \ TT_expr; \ } while(false) #endif #define TTSWAB16(TT_num) \ ( \ ((TT_num & 0x00ffU) << 8) | \ ((TT_num & 0xff00U) >> 8) \ ) #define TTSWAB32(TT_num) \ ( \ ((TT_num & 0x000000ffUL) << 24) | \ ((TT_num & 0x0000ff00UL) << 8) | \ ((TT_num & 0x00ff0000UL) >> 8) | \ ((TT_num & 0xff000000UL) >> 24) \ ) #define TTSWAB64(TT_num) \ ( \ ((TT_num & 0x00000000000000ffULL) << 56) | \ ((TT_num & 0x000000000000ff00ULL) << 40) | \ ((TT_num & 0x0000000000ff0000ULL) << 24) | \ ((TT_num & 0x00000000ff000000ULL) << 8) | \ ((TT_num & 0x000000ff00000000ULL) >> 8) | \ ((TT_num & 0x0000ff0000000000ULL) >> 24) | \ ((TT_num & 0x00ff000000000000ULL) >> 40) | \ ((TT_num & 0xff00000000000000ULL) >> 56) \ ) #if defined(_MYBIGEND) || defined(_MYSWAB) #define TTBIGEND 1 #define TTHTONS(TT_num) (TT_num) #define TTHTONL(TT_num) (TT_num) #define TTHTONLL(TT_num) (TT_num) #define TTNTOHS(TT_num) (TT_num) #define TTNTOHL(TT_num) (TT_num) #define TTNTOHLL(TT_num) (TT_num) #else #define TTBIGEND 0 #define TTHTONS(TT_num) TTSWAB16(TT_num) #define TTHTONL(TT_num) TTSWAB32(TT_num) #define TTHTONLL(TT_num) TTSWAB64(TT_num) #define TTNTOHS(TT_num) TTSWAB16(TT_num) #define TTNTOHL(TT_num) TTSWAB32(TT_num) #define TTNTOHLL(TT_num) TTSWAB64(TT_num) #endif /************************************************************************************************* * general headers *************************************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(_SYS_FREEBSD_) || defined(_SYS_MACOSX_) #define TTUSEKQUEUE 1 #elif defined(_SYS_SUNOS_) /* to check the possilibity of compilation on Linux #define port_create() (1) #define port_associate(aaa, bbb, ccc, ddd, eee) (0) #define port_dissociate(aaa, bbb, ccc) (0) typedef struct { int portev_object; } port_event_t; #define port_getn(aaa, bbb, ccc, ddd, eee) (0) #define PORT_SOURCE_FD 0 */ #include #define TTUSEEVPORTS 1 #else #include #endif /************************************************************************************************* * miscellaneous hacks *************************************************************************************************/ #if defined(_SYS_FREEBSD_) || defined(_SYS_NETBSD_) || defined(_SYS_OPENBSD_) #define nan(TC_a) strtod("nan", NULL) #define nanl(TC_a) ((long double)strtod("nan", NULL)) #endif int _tt_dummyfunc(void); int _tt_dummyfuncv(int a, ...); /************************************************************************************************* * notation of filesystems *************************************************************************************************/ #define MYPATHCHR '/' #define MYPATHSTR "/" #define MYEXTCHR '.' #define MYEXTSTR "." #define MYCDIRSTR "." #define MYPDIRSTR ".." /************************************************************************************************* * epoll emulation *************************************************************************************************/ #if defined(TTUSEKQUEUE) || defined(TTUSEEVPORTS) struct epoll_event { uint32_t events; union { void *ptr; int fd; } data; }; enum { EPOLLIN = 1 << 0, EPOLLOUT = 1 << 1, EPOLLONESHOT = 1 << 8 }; enum { EPOLL_CTL_ADD, EPOLL_CTL_MOD, EPOLL_CTL_DEL }; int _tt_epoll_create(int size); int _tt_epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); int _tt_epoll_reassoc(int epfd, int fd); int _tt_epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout); #define epoll_create _tt_epoll_create #define epoll_ctl _tt_epoll_ctl #define epoll_reassoc _tt_epoll_reassoc #define epoll_wait _tt_epoll_wait #define epoll_close close #else #define epoll_reassoc(TC_epfd, TC_fd) 0 #define epoll_close close #endif /************************************************************************************************* * utilities for implementation *************************************************************************************************/ #define TTNUMBUFSIZ 32 // size of a buffer for a number /* set a buffer for a variable length number */ #define TTSETVNUMBUF(TT_len, TT_buf, TT_num) \ do { \ int _TT_num = (TT_num); \ if(_TT_num == 0){ \ ((signed char *)(TT_buf))[0] = 0; \ (TT_len) = 1; \ } else { \ (TT_len) = 0; \ while(_TT_num > 0){ \ int _TT_rem = _TT_num & 0x7f; \ _TT_num >>= 7; \ if(_TT_num > 0){ \ ((signed char *)(TT_buf))[(TT_len)] = -_TT_rem - 1; \ } else { \ ((signed char *)(TT_buf))[(TT_len)] = _TT_rem; \ } \ (TT_len)++; \ } \ } \ } while(false) /* set a buffer for a variable length number of 64-bit */ #define TTSETVNUMBUF64(TT_len, TT_buf, TT_num) \ do { \ long long int _TT_num = (TT_num); \ if(_TT_num == 0){ \ ((signed char *)(TT_buf))[0] = 0; \ (TT_len) = 1; \ } else { \ (TT_len) = 0; \ while(_TT_num > 0){ \ int _TT_rem = _TT_num & 0x7f; \ _TT_num >>= 7; \ if(_TT_num > 0){ \ ((signed char *)(TT_buf))[(TT_len)] = -_TT_rem - 1; \ } else { \ ((signed char *)(TT_buf))[(TT_len)] = _TT_rem; \ } \ (TT_len)++; \ } \ } \ } while(false) /* read a variable length buffer */ #define TTREADVNUMBUF(TT_buf, TT_num, TT_step) \ do { \ TT_num = 0; \ int _TT_base = 1; \ int _TT_i = 0; \ while(true){ \ if(((signed char *)(TT_buf))[_TT_i] >= 0){ \ TT_num += ((signed char *)(TT_buf))[_TT_i] * _TT_base; \ break; \ } \ TT_num += _TT_base * (((signed char *)(TT_buf))[_TT_i] + 1) * -1; \ _TT_base <<= 7; \ _TT_i++; \ } \ (TT_step) = _TT_i + 1; \ } while(false) /* read a variable length buffer */ #define TTREADVNUMBUF64(TT_buf, TT_num, TT_step) \ do { \ TT_num = 0; \ long long int _TT_base = 1; \ int _TT_i = 0; \ while(true){ \ if(((signed char *)(TT_buf))[_TT_i] >= 0){ \ TT_num += ((signed char *)(TT_buf))[_TT_i] * _TT_base; \ break; \ } \ TT_num += _TT_base * (((signed char *)(TT_buf))[_TT_i] + 1) * -1; \ _TT_base <<= 7; \ _TT_i++; \ } \ (TT_step) = _TT_i + 1; \ } while(false) #endif // duplication check // END OF FILE tokyotyrant-1.1.40/scrext.c000066400000000000000000002021631133351147200156640ustar00rootroot00000000000000/************************************************************************************************* * Scripting language extension of Tokyo Tyrant * Copyright (C) 2006-2010 Mikio Hirabayashi * This file is part of Tokyo Tyrant. * Tokyo Tyrant is free software; you can redistribute it and/or modify it under the terms of * the GNU Lesser General Public License as published by the Free Software Foundation; either * version 2.1 of the License or any later version. Tokyo Tyrant 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 Lesser General Public * License for more details. * You should have received a copy of the GNU Lesser General Public License along with Tokyo * Tyrant; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA. *************************************************************************************************/ #include "scrext.h" /************************************************************************************************* * by default *************************************************************************************************/ #if defined(TTNOEXT) typedef struct _SCREXT { // type of structure of the script extension struct _SCREXT **screxts; // script extension objects int thnum; // number of native threads int thid; // thread ID char *path; // path of the initializing script TCADB *adb; // abstract database object TCULOG *ulog; // update log object uint32_t sid; // server ID TCMDB *stash; // global stash object TCMDB *lock; // global lock object void (*logger)(int, const char *, void *); // logging function void *logopq; // opaque pointer for the logging function bool term; // terminate flag } SCREXT; /* Initialize the global scripting language extension. */ void *scrextnew(void **screxts, int thnum, int thid, const char *path, TCADB *adb, TCULOG *ulog, uint32_t sid, TCMDB *stash, TCMDB *lock, void (*logger)(int, const char *, void *), void *logopq){ SCREXT *scr = tcmalloc(sizeof(*scr)); scr->screxts = (SCREXT **)screxts; scr->thnum = thnum; scr->thid = thid; scr->path = tcstrdup(path); scr->adb = adb; scr->ulog = ulog; scr->sid = sid; scr->stash = stash; scr->lock = lock; scr->logger = logger; scr->logopq = logopq; scr->term = false; return scr; } /* Destroy the global scripting language extension. */ bool scrextdel(void *scr){ SCREXT *myscr = scr; tcfree(myscr->path); tcfree(myscr); return true; } /* Call a method of the scripting language extension. */ char *scrextcallmethod(void *scr, const char *name, const void *kbuf, int ksiz, const void *vbuf, int vsiz, int *sp){ SCREXT *myscr = scr; if(!strcmp(name, "put")){ if(!tculogadbput(myscr->ulog, myscr->sid, 0, myscr->adb, kbuf, ksiz, vbuf, vsiz)) return NULL; char *msg = tcstrdup("ok"); *sp = strlen(msg); return msg; } else if(!strcmp(name, "putkeep")){ if(!tculogadbputkeep(myscr->ulog, myscr->sid, 0, myscr->adb, kbuf, ksiz, vbuf, vsiz)) return NULL; char *msg = tcstrdup("ok"); *sp = strlen(msg); return msg; } else if(!strcmp(name, "putcat")){ if(!tculogadbputcat(myscr->ulog, myscr->sid, 0, myscr->adb, kbuf, ksiz, vbuf, vsiz)) return NULL; char *msg = tcstrdup("ok"); *sp = strlen(msg); return msg; } else if(!strcmp(name, "out")){ if(!tculogadbout(myscr->ulog, myscr->sid, 0, myscr->adb, kbuf, ksiz)) return NULL; char *msg = tcstrdup("ok"); *sp = strlen(msg); return msg; } else if(!strcmp(name, "get")){ return tcadbget(myscr->adb, kbuf, ksiz, sp); } else if(!strcmp(name, "log")){ char *msg = tcmemdup(kbuf, ksiz); myscr->logger(TTLOGINFO, msg, myscr->logopq); tcfree(msg); msg = tcstrdup("ok"); *sp = strlen(msg); return msg; } int psiz = strlen(myscr->path); int nsiz = strlen(name); char *msg = tcmalloc(psiz + nsiz + ksiz + vsiz + 4); char *wp = msg; memcpy(wp, myscr->path, psiz); wp += psiz; *(wp++) = ':'; memcpy(wp, name, nsiz); wp += nsiz; *(wp++) = ':'; memcpy(wp, kbuf, ksiz); wp += ksiz; *(wp++) = ':'; memcpy(wp, vbuf, vsiz); wp += vsiz; *sp = wp - msg; return msg; } /* Send the terminate signal to the scripting language extension */ bool scrextkill(void *scr){ SCREXT *myscr = scr; myscr->term = true; return true; } #endif /************************************************************************************************* * for Lua *************************************************************************************************/ #if defined(TTLUAEXT) #include "lua.h" #include "lualib.h" #include "lauxlib.h" #define SERVVAR "_serv_" // global variable name for server resources #define ITERVAR "_iter_" // global variable name for iterator #define MRMAPVAR "_mrmap_" // global variable name for mapreduce mapper #define MRREDVAR "_mrred_" // global variable name for mapreduce reducer #define MRPOOLVAR "_mrpool_" // global variable name for mapreduce pool typedef struct { // type of structure of the script extension lua_State *lua; // Lua environment int thnum; // number of native threads int thid; // thread ID } SCREXT; typedef struct { // type of structure of the server data SCREXT **screxts; // script extension objects int thnum; // number of native threads int thid; // thread ID TCADB *adb; // abstract database object TCULOG *ulog; // update log object uint32_t sid; // server ID TCMDB *stash; // global stash object TCMDB *lock; // global lock object pthread_mutex_t *lcks; // mutex for user locks int lcknum; // number of user locks void (*logger)(int, const char *, void *); // logging function void *logopq; // opaque pointer for the logging function bool term; // terminate flag } SERV; /* private function prototypes */ static void reporterror(lua_State *lua); static bool iterrec(const void *kbuf, int ksiz, const void *vbuf, int vsiz, lua_State *lua); static int serv_eval(lua_State *lua); static int serv_log(lua_State *lua); static int serv_put(lua_State *lua); static int serv_putkeep(lua_State *lua); static int serv_putcat(lua_State *lua); static int serv_out(lua_State *lua); static int serv_get(lua_State *lua); static int serv_vsiz(lua_State *lua); static int serv_iterinit(lua_State *lua); static int serv_iternext(lua_State *lua); static int serv_fwmkeys(lua_State *lua); static int serv_addint(lua_State *lua); static int serv_adddouble(lua_State *lua); static int serv_vanish(lua_State *lua); static int serv_rnum(lua_State *lua); static int serv_size(lua_State *lua); static int serv_misc(lua_State *lua); static int serv_foreach(lua_State *lua); static int serv_mapreduce(lua_State *lua); static int serv_mapreducemapemit(lua_State *lua); static int serv_stashput(lua_State *lua); static int serv_stashputkeep(lua_State *lua); static int serv_stashputcat(lua_State *lua); static int serv_stashout(lua_State *lua); static int serv_stashget(lua_State *lua); static int serv_stashvanish(lua_State *lua); static int serv_stashforeach(lua_State *lua); static int serv_lock(lua_State *lua); static int serv_unlock(lua_State *lua); static int serv_pack(lua_State *lua); static int serv_unpack(lua_State *lua); static int serv_split(lua_State *lua); static int serv_codec(lua_State *lua); static int serv_hash(lua_State *lua); static int serv_bit(lua_State *lua); static int serv_strstr(lua_State *lua); static int serv_regex(lua_State *lua); static int serv_ucs(lua_State *lua); static int serv_dist(lua_State *lua); static int serv_isect(lua_State *lua); static int serv_union(lua_State *lua); static int serv_time(lua_State *lua); static int serv_sleep(lua_State *lua); static int serv_stat(lua_State *lua); static int serv_glob(lua_State *lua); static int serv_remove(lua_State *lua); static int serv_mkdir(lua_State *lua); /* Initialize the global scripting language extension. */ void *scrextnew(void **screxts, int thnum, int thid, const char *path, TCADB *adb, TCULOG *ulog, uint32_t sid, TCMDB *stash, TCMDB *lock, void (*logger)(int, const char *, void *), void *logopq){ char *ibuf; int isiz; if(*path == '@'){ ibuf = tcstrdup(path + 1); isiz = strlen(ibuf); } else if(*path != '\0'){ ibuf = tcreadfile(path, 0, &isiz); } else { ibuf = tcmemdup("", 0); isiz = 0; } if(!ibuf) return NULL; lua_State *lua = luaL_newstate(); if(!lua){ tcfree(ibuf); return NULL; } luaL_openlibs(lua); lua_settop(lua, 0); SERV *serv = lua_newuserdata(lua, sizeof(*serv)); serv->screxts = (SCREXT **)screxts; serv->thnum = thnum; serv->thid = thid; serv->adb = adb; serv->ulog = ulog; serv->sid = sid; serv->stash = stash; serv->lock = lock; serv->logger = logger; serv->logopq = logopq; serv->term = false; lua_setglobal(lua, SERVVAR); lua_register(lua, "_eval", serv_eval); lua_register(lua, "_log", serv_log); lua_register(lua, "_put", serv_put); lua_register(lua, "_putkeep", serv_putkeep); lua_register(lua, "_putcat", serv_putcat); lua_register(lua, "_out", serv_out); lua_register(lua, "_get", serv_get); lua_register(lua, "_vsiz", serv_vsiz); lua_register(lua, "_iterinit", serv_iterinit); lua_register(lua, "_iternext", serv_iternext); lua_register(lua, "_fwmkeys", serv_fwmkeys); lua_register(lua, "_addint", serv_addint); lua_register(lua, "_adddouble", serv_adddouble); lua_register(lua, "_vanish", serv_vanish); lua_register(lua, "_rnum", serv_rnum); lua_register(lua, "_size", serv_size); lua_register(lua, "_misc", serv_misc); lua_register(lua, "_foreach", serv_foreach); lua_register(lua, "_mapreduce", serv_mapreduce); lua_register(lua, "_stashput", serv_stashput); lua_register(lua, "_stashputkeep", serv_stashputkeep); lua_register(lua, "_stashputcat", serv_stashputcat); lua_register(lua, "_stashout", serv_stashout); lua_register(lua, "_stashget", serv_stashget); lua_register(lua, "_stashvanish", serv_stashvanish); lua_register(lua, "_stashforeach", serv_stashforeach); lua_register(lua, "_lock", serv_lock); lua_register(lua, "_unlock", serv_unlock); lua_register(lua, "_pack", serv_pack); lua_register(lua, "_unpack", serv_unpack); lua_register(lua, "_split", serv_split); lua_register(lua, "_codec", serv_codec); lua_register(lua, "_hash", serv_hash); lua_register(lua, "_bit", serv_bit); lua_register(lua, "_strstr", serv_strstr); lua_register(lua, "_regex", serv_regex); lua_register(lua, "_ucs", serv_ucs); lua_register(lua, "_dist", serv_dist); lua_register(lua, "_isect", serv_isect); lua_register(lua, "_union", serv_union); lua_register(lua, "_time", serv_time); lua_register(lua, "_sleep", serv_sleep); lua_register(lua, "_stat", serv_stat); lua_register(lua, "_glob", serv_glob); lua_register(lua, "_remove", serv_remove); lua_register(lua, "_mkdir", serv_mkdir); lua_pushstring(lua, ttversion); lua_setglobal(lua, "_version"); lua_pushinteger(lua, getpid()); lua_setglobal(lua, "_pid"); lua_pushinteger(lua, sid); lua_setglobal(lua, "_sid"); lua_pushinteger(lua, thnum); lua_setglobal(lua, "_thnum"); lua_pushinteger(lua, thid + 1); lua_setglobal(lua, "_thid"); lua_settop(lua, 0); if(luaL_loadstring(lua, ibuf) != 0 || lua_pcall(lua, 0, 0, 0) != 0) reporterror(lua); tcfree(ibuf); if(thid == 0){ lua_getglobal(lua, "_begin"); if(lua_isfunction(lua, -1) && lua_pcall(lua, 0, 0, 0) != 0) reporterror(lua); } lua_settop(lua, 0); SCREXT *scr = tcmalloc(sizeof(*scr)); scr->lua = lua; scr->thnum = thnum; scr->thid = thid; return scr; } /* Destroy the global scripting language extension. */ bool scrextdel(void *scr){ SCREXT *myscr = scr; lua_State *lua = myscr->lua; if(myscr->thid == 0){ lua_getglobal(lua, "_end"); if(lua_isfunction(lua, -1) && lua_pcall(lua, 0, 0, 0) != 0) reporterror(lua); } lua_close(lua); tcfree(scr); return true; } /* Call a method of the scripting language extension. */ char *scrextcallmethod(void *scr, const char *name, const void *kbuf, int ksiz, const void *vbuf, int vsiz, int *sp){ SCREXT *myscr = scr; lua_State *lua = myscr->lua; if(*name == '_') return NULL; lua_getglobal(lua, name); if(lua_gettop(lua) != 1 || !lua_isfunction(lua, 1)){ lua_settop(lua, 0); return NULL; } lua_pushlstring(lua, kbuf, ksiz); lua_pushlstring(lua, vbuf, vsiz); if(lua_pcall(lua, 2, 1, 0) != 0){ reporterror(lua); lua_settop(lua, 0); return NULL; } if(lua_gettop(lua) < 1) return NULL; const char *rbuf = NULL; size_t rsiz; switch(lua_type(lua, 1)){ case LUA_TNUMBER: case LUA_TSTRING: rbuf = lua_tolstring(lua, 1, &rsiz); break; case LUA_TBOOLEAN: if(lua_toboolean(lua, 1)){ rbuf = "true"; rsiz = strlen(rbuf); } break; case LUA_TTABLE: if(lua_objlen(lua, 1) > 0){ lua_rawgeti(lua, 1, 1); switch(lua_type(lua, -1)){ case LUA_TNUMBER: case LUA_TSTRING: rbuf = lua_tolstring(lua, -1, &rsiz); break; case LUA_TBOOLEAN: if(lua_toboolean(lua, -1)){ rbuf = "true"; rsiz = strlen(rbuf); } break; } lua_pop(lua, 1); } break; } if(!rbuf){ lua_settop(lua, 0); return NULL; } char *rv = tcmemdup(rbuf, rsiz); *sp = rsiz; lua_settop(lua, 0); return rv; } /* Send the terminate signal to the scripting language extension */ bool scrextkill(void *scr){ SCREXT *myscr = scr; lua_State *lua = myscr->lua; lua_getglobal(lua, SERVVAR); SERV *serv = lua_touserdata(lua, -1); serv->term = true; return true; } /* report an error of Lua program */ static void reporterror(lua_State *lua){ int argc = lua_gettop(lua); char *msg = tcsprintf("Lua error: %s", argc > 0 ? lua_tostring(lua, argc) : "unknown"); lua_getglobal(lua, SERVVAR); SERV *serv = lua_touserdata(lua, -1); serv->logger(TTLOGERROR, msg, serv->logopq); tcfree(msg); } /* call function for each record */ static bool iterrec(const void *kbuf, int ksiz, const void *vbuf, int vsiz, lua_State *lua){ int top = lua_gettop(lua); lua_getglobal(lua, ITERVAR); lua_pushlstring(lua, kbuf, ksiz); lua_pushlstring(lua, vbuf, vsiz); bool err = false; if(lua_pcall(lua, 2, 1, 0) == 0){ if(lua_gettop(lua) <= top || !lua_toboolean(lua, -1)) err = true; } else { reporterror(lua); err = true; } lua_settop(lua, top); return !err; } /* call function to map records for mapreduce */ static bool maprec(void *map, const void *kbuf, int ksiz, const void *vbuf, int vsiz, lua_State *lua){ lua_pushlightuserdata(lua, map); lua_setglobal(lua, MRPOOLVAR); int top = lua_gettop(lua); lua_getglobal(lua, MRMAPVAR); lua_pushlstring(lua, kbuf, ksiz); lua_pushlstring(lua, vbuf, vsiz); lua_pushcfunction(lua, serv_mapreducemapemit); bool err = false; if(lua_pcall(lua, 3, 1, 0) == 0){ if(lua_gettop(lua) < 1 && !lua_toboolean(lua, 1)) err = true; } else { reporterror(lua); err = true; } lua_settop(lua, top); return !err; } /* for _eval function */ static int serv_eval(lua_State *lua){ int argc = lua_gettop(lua); if(argc != 1){ lua_pushstring(lua, "_eval: invalid arguments"); lua_error(lua); } const char *expr = lua_tostring(lua, 1); if(!expr){ lua_pushstring(lua, "_eval: invalid arguments"); lua_error(lua); } lua_getglobal(lua, SERVVAR); SERV *serv = lua_touserdata(lua, -1); SCREXT **screxts = serv->screxts; int thnum = serv->thnum; bool err = false; for(int i = 0; i < thnum; i++){ if(!screxts[i]){ lua_pushstring(lua, "_eval: not ready"); lua_error(lua); } } for(int i = 0; i < thnum; i++){ lua_State *elua = screxts[i]->lua; if(luaL_loadstring(elua, expr) != 0 || lua_pcall(elua, 0, 0, 0) != 0) reporterror(elua); } lua_settop(lua, 0); lua_pushboolean(lua, !err); return 1; } /* for _log function */ static int serv_log(lua_State *lua){ int argc = lua_gettop(lua); if(argc < 1){ lua_pushstring(lua, "_log: invalid arguments"); lua_error(lua); } const char *msg = lua_tostring(lua, 1); if(!msg){ lua_pushstring(lua, "_log: invalid arguments"); lua_error(lua); } int level = TTLOGINFO; if(argc > 1) level = lua_tointeger(lua, 2); lua_getglobal(lua, SERVVAR); SERV *serv = lua_touserdata(lua, -1); serv->logger(level, msg, serv->logopq); lua_settop(lua, 0); return 0; } /* for _put function */ static int serv_put(lua_State *lua){ int argc = lua_gettop(lua); if(argc != 2){ lua_pushstring(lua, "_put: invalid arguments"); lua_error(lua); } size_t ksiz; const char *kbuf = lua_tolstring(lua, 1, &ksiz); size_t vsiz; const char *vbuf = lua_tolstring(lua, 2, &vsiz); if(!kbuf || !vbuf){ lua_pushstring(lua, "_put: invalid arguments"); lua_error(lua); } lua_getglobal(lua, SERVVAR); SERV *serv = lua_touserdata(lua, -1); bool rv = tculogadbput(serv->ulog, serv->sid, 0, serv->adb, kbuf, ksiz, vbuf, vsiz); lua_settop(lua, 0); lua_pushboolean(lua, rv); return 1; } /* for _putkeep function */ static int serv_putkeep(lua_State *lua){ int argc = lua_gettop(lua); if(argc != 2){ lua_pushstring(lua, "_putkeep: invalid arguments"); lua_error(lua); } size_t ksiz; const char *kbuf = lua_tolstring(lua, 1, &ksiz); size_t vsiz; const char *vbuf = lua_tolstring(lua, 2, &vsiz); if(!kbuf || !vbuf){ lua_pushstring(lua, "_putkeep: invalid arguments"); lua_error(lua); } lua_getglobal(lua, SERVVAR); SERV *serv = lua_touserdata(lua, -1); bool rv = tculogadbputkeep(serv->ulog, serv->sid, 0, serv->adb, kbuf, ksiz, vbuf, vsiz); lua_settop(lua, 0); lua_pushboolean(lua, rv); return 1; } /* for _putcat function */ static int serv_putcat(lua_State *lua){ int argc = lua_gettop(lua); if(argc != 2){ lua_pushstring(lua, "_putcat: invalid arguments"); lua_error(lua); } size_t ksiz; const char *kbuf = lua_tolstring(lua, 1, &ksiz); size_t vsiz; const char *vbuf = lua_tolstring(lua, 2, &vsiz); if(!kbuf || !vbuf){ lua_pushstring(lua, "_putcat: invalid arguments"); lua_error(lua); } lua_getglobal(lua, SERVVAR); SERV *serv = lua_touserdata(lua, -1); bool rv = tculogadbputcat(serv->ulog, serv->sid, 0, serv->adb, kbuf, ksiz, vbuf, vsiz); lua_settop(lua, 0); lua_pushboolean(lua, rv); return 1; } /* for _putout function */ static int serv_out(lua_State *lua){ int argc = lua_gettop(lua); if(argc != 1){ lua_pushstring(lua, "_out: invalid arguments"); lua_error(lua); } size_t ksiz; const char *kbuf = lua_tolstring(lua, 1, &ksiz); if(!kbuf){ lua_pushstring(lua, "_out: invalid arguments"); lua_error(lua); } lua_getglobal(lua, SERVVAR); SERV *serv = lua_touserdata(lua, -1); bool rv = tculogadbout(serv->ulog, serv->sid, 0, serv->adb, kbuf, ksiz); lua_settop(lua, 0); lua_pushboolean(lua, rv); return 1; } /* for _get function */ static int serv_get(lua_State *lua){ int argc = lua_gettop(lua); if(argc != 1){ lua_pushstring(lua, "_get: invalid arguments"); lua_error(lua); } size_t ksiz; const char *kbuf = lua_tolstring(lua, 1, &ksiz); if(!kbuf){ lua_pushstring(lua, "_get: invalid arguments"); lua_error(lua); } lua_getglobal(lua, SERVVAR); SERV *serv = lua_touserdata(lua, -1); int vsiz; char *vbuf = tcadbget(serv->adb, kbuf, ksiz, &vsiz); lua_settop(lua, 0); if(vbuf){ lua_pushlstring(lua, vbuf, vsiz); tcfree(vbuf); } else { lua_pushnil(lua); } return 1; } /* for _vsiz function */ static int serv_vsiz(lua_State *lua){ int argc = lua_gettop(lua); if(argc != 1){ lua_pushstring(lua, "_vsiz: invalid arguments"); lua_error(lua); } size_t ksiz; const char *kbuf = lua_tolstring(lua, 1, &ksiz); if(!kbuf){ lua_pushstring(lua, "_vsiz: invalid arguments"); lua_error(lua); } lua_getglobal(lua, SERVVAR); SERV *serv = lua_touserdata(lua, -1); int vsiz = tcadbvsiz(serv->adb, kbuf, ksiz); lua_settop(lua, 0); lua_pushnumber(lua, vsiz); return 1; } /* for _iterinit function */ static int serv_iterinit(lua_State *lua){ int argc = lua_gettop(lua); if(argc != 0){ lua_pushstring(lua, "_iterinit: invalid arguments"); lua_error(lua); } lua_getglobal(lua, SERVVAR); SERV *serv = lua_touserdata(lua, -1); bool rv = tcadbiterinit(serv->adb); lua_settop(lua, 0); lua_pushboolean(lua, rv); return 1; } /* for _iternext function */ static int serv_iternext(lua_State *lua){ int argc = lua_gettop(lua); if(argc != 0){ lua_pushstring(lua, "_iternext: invalid arguments"); lua_error(lua); } lua_getglobal(lua, SERVVAR); SERV *serv = lua_touserdata(lua, -1); int vsiz; char *vbuf = tcadbiternext(serv->adb, &vsiz); lua_settop(lua, 0); if(vbuf){ lua_pushlstring(lua, vbuf, vsiz); tcfree(vbuf); } else { lua_pushnil(lua); } return 1; } /* for _fwmkeys function */ static int serv_fwmkeys(lua_State *lua){ int argc = lua_gettop(lua); if(argc < 1){ lua_pushstring(lua, "_fwmkeys: invalid arguments"); lua_error(lua); } size_t psiz; const char *pbuf = lua_tolstring(lua, 1, &psiz); if(!pbuf){ lua_pushstring(lua, "_fwmkeys: invalid arguments"); lua_error(lua); } int max = argc > 1 && lua_isnumber(lua, 2) ? lua_tonumber(lua, 2) : -1; lua_getglobal(lua, SERVVAR); SERV *serv = lua_touserdata(lua, -1); TCLIST *keys = tcadbfwmkeys(serv->adb, pbuf, psiz, max); lua_settop(lua, 0); int knum = tclistnum(keys); lua_createtable(lua, knum, 0); for(int i = 0; i < knum; i++){ int ksiz; const char *kbuf = tclistval(keys, i, &ksiz); lua_pushlstring(lua, kbuf, ksiz); lua_rawseti(lua, 1, i + 1); } tclistdel(keys); return 1; } /* for _addint function */ static int serv_addint(lua_State *lua){ int argc = lua_gettop(lua); if(argc != 2){ lua_pushstring(lua, "_addint: invalid arguments"); lua_error(lua); } size_t ksiz; const char *kbuf = lua_tolstring(lua, 1, &ksiz); int num = lua_tonumber(lua, 2); if(!kbuf || !lua_isnumber(lua, 2)){ lua_pushstring(lua, "_addint: invalid arguments"); lua_error(lua); } lua_getglobal(lua, SERVVAR); SERV *serv = lua_touserdata(lua, -1); int rv = tculogadbaddint(serv->ulog, serv->sid, 0, serv->adb, kbuf, ksiz, num); lua_settop(lua, 0); if(rv == INT_MIN){ lua_pushnil(lua); } else { lua_pushnumber(lua, rv); } return 1; } /* for _adddouble function */ static int serv_adddouble(lua_State *lua){ int argc = lua_gettop(lua); if(argc != 2){ lua_pushstring(lua, "_adddouble: invalid arguments"); lua_error(lua); } size_t ksiz; const char *kbuf = lua_tolstring(lua, 1, &ksiz); double num = lua_tonumber(lua, 2); if(!kbuf || !lua_isnumber(lua, 2)){ lua_pushstring(lua, "_adddouble: invalid arguments"); lua_error(lua); } lua_getglobal(lua, SERVVAR); SERV *serv = lua_touserdata(lua, -1); double rv = tculogadbadddouble(serv->ulog, serv->sid, 0, serv->adb, kbuf, ksiz, num); lua_settop(lua, 0); if(isnan(rv)){ lua_pushnil(lua); } else { lua_pushnumber(lua, rv); } return 1; } /* for _vanish function */ static int serv_vanish(lua_State *lua){ int argc = lua_gettop(lua); if(argc != 0){ lua_pushstring(lua, "_vanish: invalid arguments"); lua_error(lua); } lua_getglobal(lua, SERVVAR); SERV *serv = lua_touserdata(lua, -1); bool rv = tculogadbvanish(serv->ulog, serv->sid, 0, serv->adb); lua_settop(lua, 0); lua_pushboolean(lua, rv); return 1; } /* for _rnum function */ static int serv_rnum(lua_State *lua){ int argc = lua_gettop(lua); if(argc != 0){ lua_pushstring(lua, "_rnum: invalid arguments"); lua_error(lua); } lua_getglobal(lua, SERVVAR); SERV *serv = lua_touserdata(lua, -1); uint64_t rnum = tcadbrnum(serv->adb); lua_settop(lua, 0); lua_pushnumber(lua, rnum); return 1; } /* for _size function */ static int serv_size(lua_State *lua){ int argc = lua_gettop(lua); if(argc != 0){ lua_pushstring(lua, "_size: invalid arguments"); lua_error(lua); } lua_getglobal(lua, SERVVAR); SERV *serv = lua_touserdata(lua, -1); uint64_t size = tcadbsize(serv->adb); lua_settop(lua, 0); lua_pushnumber(lua, size); return 1; } /* for _misc function */ static int serv_misc(lua_State *lua){ int argc = lua_gettop(lua); if(argc < 1){ lua_pushstring(lua, "_misc: invalid arguments"); lua_error(lua); } const char *name = lua_tostring(lua, 1); if(!name){ lua_pushstring(lua, "_misc: invalid arguments"); lua_error(lua); } bool ulog = true; if(*name == '$'){ name++; ulog = false; } TCLIST *args = tclistnew(); for(int i = 2; i <= argc; i++){ const char *aptr; size_t asiz; int len; switch(lua_type(lua, i)){ case LUA_TNUMBER: case LUA_TSTRING: aptr = lua_tolstring(lua, i, &asiz); tclistpush(args, aptr, asiz); break; case LUA_TTABLE: len = lua_objlen(lua, i); for(int j = 1; j <= len; j++){ lua_rawgeti(lua, i, j); switch(lua_type(lua, -1)){ case LUA_TNUMBER: case LUA_TSTRING: aptr = lua_tolstring(lua, -1, &asiz); tclistpush(args, aptr, asiz); break; } lua_pop(lua, 1); } break; } } lua_getglobal(lua, SERVVAR); SERV *serv = lua_touserdata(lua, -1); TCLIST *res = ulog ? tculogadbmisc(serv->ulog, serv->sid, 0, serv->adb, name, args) : tcadbmisc(serv->adb, name, args); lua_settop(lua, 0); if(res){ int rnum = tclistnum(res); lua_createtable(lua, rnum, 0); for(int i = 0; i < rnum; i++){ int rsiz; const char *rbuf = tclistval(res, i, &rsiz); lua_pushlstring(lua, rbuf, rsiz); lua_rawseti(lua, 1, i + 1); } tclistdel(res); } else { lua_pushnil(lua); } tclistdel(args); return 1; } /* for _foreach function */ static int serv_foreach(lua_State *lua){ int argc = lua_gettop(lua); if(argc != 1){ lua_pushstring(lua, "_foreach: invalid arguments"); lua_error(lua); } if(!lua_isfunction(lua, 1)){ lua_pushstring(lua, "_foreach: invalid arguments"); lua_error(lua); } lua_getglobal(lua, SERVVAR); SERV *serv = lua_touserdata(lua, -1); lua_pushvalue(lua, 1); lua_setglobal(lua, ITERVAR); bool err = false; if(!tcadbforeach(serv->adb, (TCITER)iterrec, lua)) err = true; lua_pushnil(lua); lua_setglobal(lua, ITERVAR); lua_settop(lua, 0); lua_pushboolean(lua, !err); return 1; } /* for _mapreduce function */ static int serv_mapreduce(lua_State *lua){ int argc = lua_gettop(lua); if(argc < 2){ lua_pushstring(lua, "_mapreduce: invalid arguments"); lua_error(lua); } if(!lua_isfunction(lua, 1) || !lua_isfunction(lua, 2)){ lua_pushstring(lua, "_mapreduce: invalid arguments"); lua_error(lua); } lua_pushvalue(lua, 1); lua_setglobal(lua, MRMAPVAR); lua_pushvalue(lua, 2); lua_setglobal(lua, MRREDVAR); TCLIST *keys = NULL; if(argc > 2){ const char *kbuf; size_t ksiz; int len; switch(lua_type(lua, 3)){ case LUA_TNUMBER: case LUA_TSTRING: keys = tclistnew2(1); kbuf = lua_tolstring(lua, 3, &ksiz); tclistpush(keys, kbuf, ksiz); break; case LUA_TTABLE: len = lua_objlen(lua, 3); keys = tclistnew2(len); for(int i = 1; i <= len; i++){ lua_rawgeti(lua, 3, i); switch(lua_type(lua, -1)){ case LUA_TNUMBER: case LUA_TSTRING: kbuf = lua_tolstring(lua, -1, &ksiz); tclistpush(keys, kbuf, ksiz); break; } lua_pop(lua, 1); } break; } } lua_getglobal(lua, SERVVAR); SERV *serv = lua_touserdata(lua, -1); bool err = false; TCBDB *bdb = tcbdbnew(); lua_getglobal(lua, "_tmpdir_"); const char *tmpdir = lua_tostring(lua, -1); if(!tmpdir) tmpdir = "/tmp"; char *path = tcsprintf("%s%c%s-%d-%u", tmpdir, MYPATHCHR, "mapbdb", getpid(), (unsigned int)(tctime() * 1000)); unlink(path); if(!tcbdbopen(bdb, path, BDBOWRITER | BDBOCREAT | BDBOTRUNC)) err = true; unlink(path); tcfree(path); if(!tcadbmapbdb(serv->adb, keys, bdb, (ADBMAPPROC)maprec, lua, -1)) err = true; if(!err){ BDBCUR *cur = tcbdbcurnew(bdb); tcbdbcurfirst(cur); const char *lbuf = NULL; int lsiz = 0; int lnum = 0; const char *kbuf; int ksiz; while(!err && (kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL){ int vsiz; const char *vbuf = tcbdbcurval3(cur, &vsiz); if(lbuf && lsiz == ksiz && !memcmp(lbuf, kbuf, lsiz)){ lua_pushlstring(lua, vbuf, vsiz); lua_rawseti(lua, -2, ++lnum); } else { if(lbuf){ if(lua_pcall(lua, 2, 1, 0) != 0){ reporterror(lua); err = true; } else if(lua_gettop(lua) < 1 || !lua_toboolean(lua, 1)){ err = true; } } lua_settop(lua, 0); lua_getglobal(lua, MRREDVAR); lua_pushlstring(lua, kbuf, ksiz); lua_newtable(lua); lnum = 1; lua_pushlstring(lua, vbuf, vsiz); lua_rawseti(lua, -2, lnum); } lbuf = kbuf; lsiz = ksiz; tcbdbcurnext(cur); } if(lbuf){ if(lua_pcall(lua, 2, 1, 0) != 0){ reporterror(lua); err = true; } else if(lua_gettop(lua) < 1 || !lua_toboolean(lua, 1)){ err = true; } lua_settop(lua, 0); } tcbdbcurdel(cur); } if(!tcbdbclose(bdb)) err = true; tcbdbdel(bdb); if(keys) tclistdel(keys); lua_pushnil(lua); lua_setglobal(lua, MRREDVAR); lua_pushnil(lua); lua_setglobal(lua, MRMAPVAR); lua_settop(lua, 0); lua_pushboolean(lua, !err); return 1; } /* for _mapreduce function */ static int serv_mapreducemapemit(lua_State *lua){ int argc = lua_gettop(lua); if(argc != 2){ lua_pushstring(lua, "_mapreducemapemit: invalid arguments"); lua_error(lua); } size_t ksiz; const char *kbuf = lua_tolstring(lua, 1, &ksiz); size_t vsiz; const char *vbuf = lua_tolstring(lua, 2, &vsiz); if(!kbuf || !vbuf){ lua_pushstring(lua, "_mapreducemapemit: invalid arguments"); lua_error(lua); } lua_getglobal(lua, MRPOOLVAR); void *map = lua_touserdata(lua, -1); bool rv = tcadbmapbdbemit(map, kbuf, ksiz, vbuf, vsiz); lua_settop(lua, 0); lua_pushboolean(lua, rv); return 1; } /* for _stashput function */ static int serv_stashput(lua_State *lua){ int argc = lua_gettop(lua); if(argc != 2){ lua_pushstring(lua, "_stashput: invalid arguments"); lua_error(lua); } size_t ksiz; const char *kbuf = lua_tolstring(lua, 1, &ksiz); size_t vsiz; const char *vbuf = lua_tolstring(lua, 2, &vsiz); if(!kbuf || !vbuf){ lua_pushstring(lua, "_stashput: invalid arguments"); lua_error(lua); } lua_getglobal(lua, SERVVAR); SERV *serv = lua_touserdata(lua, -1); tcmdbput(serv->stash, kbuf, ksiz, vbuf, vsiz); lua_pushboolean(lua, true); return 1; } /* for _stashputkeep function */ static int serv_stashputkeep(lua_State *lua){ int argc = lua_gettop(lua); if(argc != 2){ lua_pushstring(lua, "_stashputkeep: invalid arguments"); lua_error(lua); } size_t ksiz; const char *kbuf = lua_tolstring(lua, 1, &ksiz); size_t vsiz; const char *vbuf = lua_tolstring(lua, 2, &vsiz); if(!kbuf || !vbuf){ lua_pushstring(lua, "_stashputkeep: invalid arguments"); lua_error(lua); } lua_getglobal(lua, SERVVAR); SERV *serv = lua_touserdata(lua, -1); tcmdbputkeep(serv->stash, kbuf, ksiz, vbuf, vsiz); lua_pushboolean(lua, true); return 1; } /* for _stashputcat function */ static int serv_stashputcat(lua_State *lua){ int argc = lua_gettop(lua); if(argc != 2){ lua_pushstring(lua, "_stashputcat: invalid arguments"); lua_error(lua); } size_t ksiz; const char *kbuf = lua_tolstring(lua, 1, &ksiz); size_t vsiz; const char *vbuf = lua_tolstring(lua, 2, &vsiz); if(!kbuf || !vbuf){ lua_pushstring(lua, "_stashputcat: invalid arguments"); lua_error(lua); } lua_getglobal(lua, SERVVAR); SERV *serv = lua_touserdata(lua, -1); tcmdbputcat(serv->stash, kbuf, ksiz, vbuf, vsiz); lua_pushboolean(lua, true); return 1; } /* for _stashout function */ static int serv_stashout(lua_State *lua){ int argc = lua_gettop(lua); if(argc != 1){ lua_pushstring(lua, "_stashout: invalid arguments"); lua_error(lua); } size_t ksiz; const char *kbuf = lua_tolstring(lua, 1, &ksiz); if(!kbuf){ lua_pushstring(lua, "_stashout: invalid arguments"); lua_error(lua); } lua_getglobal(lua, SERVVAR); SERV *serv = lua_touserdata(lua, -1); bool rv = tcmdbout(serv->stash, kbuf, ksiz); lua_pushboolean(lua, rv); return 1; } /* for _stashget function */ static int serv_stashget(lua_State *lua){ int argc = lua_gettop(lua); if(argc != 1){ lua_pushstring(lua, "_stashget: invalid arguments"); lua_error(lua); } size_t ksiz; const char *kbuf = lua_tolstring(lua, 1, &ksiz); if(!kbuf){ lua_pushstring(lua, "_stashget: invalid arguments"); lua_error(lua); } lua_getglobal(lua, SERVVAR); SERV *serv = lua_touserdata(lua, -1); int vsiz; char *vbuf = tcmdbget(serv->stash, kbuf, ksiz, &vsiz); if(vbuf){ lua_pushlstring(lua, vbuf, vsiz); tcfree(vbuf); } else { lua_pushnil(lua); } return 1; } /* for _stashvanish function */ static int serv_stashvanish(lua_State *lua){ int argc = lua_gettop(lua); if(argc != 0){ lua_pushstring(lua, "_stashvanish: invalid arguments"); lua_error(lua); } lua_getglobal(lua, SERVVAR); SERV *serv = lua_touserdata(lua, -1); tcmdbvanish(serv->stash); return 0; } /* for _stashforeach function */ static int serv_stashforeach(lua_State *lua){ int argc = lua_gettop(lua); if(argc != 1){ lua_pushstring(lua, "_stashforeach: invalid arguments"); lua_error(lua); } if(!lua_isfunction(lua, 1)){ lua_pushstring(lua, "_stashforeach: invalid arguments"); lua_error(lua); } lua_getglobal(lua, SERVVAR); SERV *serv = lua_touserdata(lua, -1); lua_pushvalue(lua, 1); lua_setglobal(lua, ITERVAR); tcmdbforeach(serv->stash, (TCITER)iterrec, lua); lua_pushnil(lua); lua_setglobal(lua, ITERVAR); return 0; } /* for _lock function */ static int serv_lock(lua_State *lua){ int argc = lua_gettop(lua); if(argc != 1){ lua_pushstring(lua, "_lock: invalid arguments"); lua_error(lua); } size_t ksiz; const char *kbuf = lua_tolstring(lua, 1, &ksiz); if(!kbuf){ lua_pushstring(lua, "_lock: invalid arguments"); lua_error(lua); } lua_getglobal(lua, SERVVAR); SERV *serv = lua_touserdata(lua, -1); bool rv = true; while(!tcmdbputkeep(serv->lock, kbuf, ksiz, "", 0)){ tcsleep(0.1); if(serv->term){ rv = false; break; } } lua_settop(lua, 0); lua_pushboolean(lua, rv); return 1; } /* for _unlock function */ static int serv_unlock(lua_State *lua){ int argc = lua_gettop(lua); if(argc != 1){ lua_pushstring(lua, "_unlock: invalid arguments"); lua_error(lua); } size_t ksiz; const char *kbuf = lua_tolstring(lua, 1, &ksiz); if(!kbuf){ lua_pushstring(lua, "_unlock: invalid arguments"); lua_error(lua); } lua_getglobal(lua, SERVVAR); SERV *serv = lua_touserdata(lua, -1); bool rv = tcmdbout(serv->lock, kbuf, ksiz); lua_settop(lua, 0); lua_pushboolean(lua, rv); return 1; } /* for _pack function */ static int serv_pack(lua_State *lua){ int argc = lua_gettop(lua); if(argc < 1){ lua_pushstring(lua, "_pack: invalid arguments"); lua_error(lua); } const char *format = lua_tostring(lua, 1); if(!format){ lua_pushstring(lua, "_pack: invalid arguments"); lua_error(lua); } lua_newtable(lua); int aidx = argc + 1; int eidx = 1; for(int i = 2; i <= argc; i++){ int len; switch(lua_type(lua, i)){ case LUA_TNUMBER: case LUA_TSTRING: lua_pushvalue(lua, i); lua_rawseti(lua, aidx, eidx++); break; case LUA_TTABLE: len = lua_objlen(lua, i); for(int j = 1; j <= len; j++){ lua_rawgeti(lua, i, j); lua_rawseti(lua, aidx, eidx++); } break; default: lua_pushnumber(lua, 0); lua_rawseti(lua, aidx, eidx++); break; } } lua_replace(lua, 2); lua_settop(lua, 2); TCXSTR *xstr = tcxstrnew(); int emax = eidx - 1; eidx = 1; while(*format != '\0'){ int c = *format; int loop = 1; if(format[1] == '*'){ loop = INT_MAX; format++; } else if(format[1] >= '0' && format[1] <= '9'){ format++; loop = 0; while(*format >= '0' && *format <= '9'){ loop = loop * 10 + *format - '0'; format++; } format--; } loop = tclmin(loop, emax); int end = tclmin(eidx + loop - 1, emax); while(eidx <= end){ lua_rawgeti(lua, 2, eidx); double num = lua_tonumber(lua, 3); lua_pop(lua, 1); uint8_t cnum; uint16_t snum; uint32_t inum; uint64_t lnum; double dnum; float fnum; uint64_t wnum; char wbuf[TTNUMBUFSIZ], *wp; switch(c){ case 'c': case 'C': cnum = num; tcxstrcat(xstr, &cnum, sizeof(cnum)); break; case 's': case 'S': snum = num; tcxstrcat(xstr, &snum, sizeof(snum)); break; case 'i': case 'I': inum = num; tcxstrcat(xstr, &inum, sizeof(inum)); break; case 'l': case 'L': lnum = num; tcxstrcat(xstr, &lnum, sizeof(lnum)); break; case 'f': case 'F': fnum = num; tcxstrcat(xstr, &fnum, sizeof(fnum)); break; case 'd': case 'D': dnum = num; tcxstrcat(xstr, &dnum, sizeof(dnum)); break; case 'n': snum = num; snum = TTHTONS(snum); tcxstrcat(xstr, &snum, sizeof(snum)); break; case 'N': inum = num; inum = TTHTONL(inum); tcxstrcat(xstr, &inum, sizeof(inum)); break; case 'M': lnum = num; lnum = TTHTONLL(lnum); tcxstrcat(xstr, &lnum, sizeof(lnum)); break; case 'w': case 'W': wnum = num; wp = wbuf; if(wnum < (1ULL << 7)){ *(wp++) = wnum; } else if(wnum < (1ULL << 14)){ *(wp++) = (wnum >> 7) | 0x80; *(wp++) = wnum & 0x7f; } else if(wnum < (1ULL << 21)){ *(wp++) = (wnum >> 14) | 0x80; *(wp++) = ((wnum >> 7) & 0x7f) | 0x80; *(wp++) = wnum & 0x7f; } else if(wnum < (1ULL << 28)){ *(wp++) = (wnum >> 21) | 0x80; *(wp++) = ((wnum >> 14) & 0x7f) | 0x80; *(wp++) = ((wnum >> 7) & 0x7f) | 0x80; *(wp++) = wnum & 0x7f; } else if(wnum < (1ULL << 35)){ *(wp++) = (wnum >> 28) | 0x80; *(wp++) = ((wnum >> 21) & 0x7f) | 0x80; *(wp++) = ((wnum >> 14) & 0x7f) | 0x80; *(wp++) = ((wnum >> 7) & 0x7f) | 0x80; *(wp++) = wnum & 0x7f; } else if(wnum < (1ULL << 42)){ *(wp++) = (wnum >> 35) | 0x80; *(wp++) = ((wnum >> 28) & 0x7f) | 0x80; *(wp++) = ((wnum >> 21) & 0x7f) | 0x80; *(wp++) = ((wnum >> 14) & 0x7f) | 0x80; *(wp++) = ((wnum >> 7) & 0x7f) | 0x80; *(wp++) = wnum & 0x7f; } else if(wnum < (1ULL << 49)){ *(wp++) = (wnum >> 42) | 0x80; *(wp++) = ((wnum >> 35) & 0x7f) | 0x80; *(wp++) = ((wnum >> 28) & 0x7f) | 0x80; *(wp++) = ((wnum >> 21) & 0x7f) | 0x80; *(wp++) = ((wnum >> 14) & 0x7f) | 0x80; *(wp++) = ((wnum >> 7) & 0x7f) | 0x80; *(wp++) = wnum & 0x7f; } else if(wnum < (1ULL << 56)){ *(wp++) = (wnum >> 49) | 0x80; *(wp++) = ((wnum >> 42) & 0x7f) | 0x80; *(wp++) = ((wnum >> 35) & 0x7f) | 0x80; *(wp++) = ((wnum >> 28) & 0x7f) | 0x80; *(wp++) = ((wnum >> 21) & 0x7f) | 0x80; *(wp++) = ((wnum >> 14) & 0x7f) | 0x80; *(wp++) = ((wnum >> 7) & 0x7f) | 0x80; *(wp++) = wnum & 0x7f; } else { *(wp++) = (wnum >> 63) | 0x80; *(wp++) = ((wnum >> 49) & 0x7f) | 0x80; *(wp++) = ((wnum >> 42) & 0x7f) | 0x80; *(wp++) = ((wnum >> 35) & 0x7f) | 0x80; *(wp++) = ((wnum >> 28) & 0x7f) | 0x80; *(wp++) = ((wnum >> 21) & 0x7f) | 0x80; *(wp++) = ((wnum >> 14) & 0x7f) | 0x80; *(wp++) = ((wnum >> 7) & 0x7f) | 0x80; *(wp++) = wnum & 0x7f; } tcxstrcat(xstr, wbuf, wp - wbuf); break; } eidx++; } format++; if(eidx > emax) break; } lua_settop(lua, 0); lua_pushlstring(lua, tcxstrptr(xstr), tcxstrsize(xstr)); tcxstrdel(xstr); return 1; } /* for _unpack function */ static int serv_unpack(lua_State *lua){ int argc = lua_gettop(lua); if(argc != 2){ lua_pushstring(lua, "_unpack: invalid arguments"); lua_error(lua); } const char *format = lua_tostring(lua, 1); size_t size; const char *buf = lua_tolstring(lua, 2, &size); if(!format){ lua_pushstring(lua, "_unpack: invalid arguments"); lua_error(lua); } if(!buf){ buf = ""; size = 0; } lua_newtable(lua); const char *rp = buf; int eidx = 1; while(*format != '\0'){ int c = *format; int loop = 1; if(format[1] == '*'){ loop = INT_MAX; format++; } else if(format[1] >= '0' && format[1] <= '9'){ format++; loop = 0; while(*format >= '0' && *format <= '9'){ loop = loop * 10 + *format - '0'; format++; } format--; } loop = tclmin(loop, size); for(int i = 0; i < loop && size > 0; i++){ uint8_t cnum; uint16_t snum; uint32_t inum; uint64_t lnum; float fnum; double dnum; uint64_t wnum; switch(c){ case 'c': if(size >= sizeof(cnum)){ memcpy(&cnum, rp, sizeof(cnum)); lua_pushnumber(lua, (int8_t)cnum); lua_rawseti(lua, 3, eidx++); rp += sizeof(cnum); size -= sizeof(cnum); } else { size = 0; } break; case 'C': if(size >= sizeof(cnum)){ memcpy(&cnum, rp, sizeof(cnum)); lua_pushnumber(lua, (uint8_t)cnum); lua_rawseti(lua, 3, eidx++); rp += sizeof(cnum); size -= sizeof(cnum); } else { size = 0; } break; case 's': if(size >= sizeof(snum)){ memcpy(&snum, rp, sizeof(snum)); lua_pushnumber(lua, (int16_t)snum); lua_rawseti(lua, 3, eidx++); rp += sizeof(snum); size -= sizeof(snum); } else { size = 0; } break; case 'S': if(size >= sizeof(snum)){ memcpy(&snum, rp, sizeof(snum)); lua_pushnumber(lua, (uint16_t)snum); lua_rawseti(lua, 3, eidx++); rp += sizeof(snum); size -= sizeof(snum); } else { size = 0; } break; case 'i': if(size >= sizeof(inum)){ memcpy(&inum, rp, sizeof(inum)); lua_pushnumber(lua, (int32_t)inum); lua_rawseti(lua, 3, eidx++); rp += sizeof(inum); size -= sizeof(inum); } else { size = 0; } break; case 'I': if(size >= sizeof(inum)){ memcpy(&inum, rp, sizeof(inum)); lua_pushnumber(lua, (uint32_t)inum); lua_rawseti(lua, 3, eidx++); rp += sizeof(inum); size -= sizeof(inum); } else { size = 0; } break; case 'l': if(size >= sizeof(lnum)){ memcpy(&lnum, rp, sizeof(lnum)); lua_pushnumber(lua, (int64_t)lnum); lua_rawseti(lua, 3, eidx++); rp += sizeof(lnum); size -= sizeof(lnum); } else { size = 0; } break; case 'L': if(size >= sizeof(lnum)){ memcpy(&lnum, rp, sizeof(lnum)); lua_pushnumber(lua, (uint64_t)lnum); lua_rawseti(lua, 3, eidx++); rp += sizeof(lnum); size -= sizeof(lnum); } else { size = 0; } break; case 'f': case 'F': if(size >= sizeof(fnum)){ memcpy(&fnum, rp, sizeof(fnum)); lua_pushnumber(lua, (float)fnum); lua_rawseti(lua, 3, eidx++); rp += sizeof(fnum); size -= sizeof(fnum); } else { size = 0; } break; case 'd': case 'D': if(size >= sizeof(dnum)){ memcpy(&dnum, rp, sizeof(dnum)); lua_pushnumber(lua, (double)dnum); lua_rawseti(lua, 3, eidx++); rp += sizeof(dnum); size -= sizeof(dnum); } else { size = 0; } break; case 'n': if(size >= sizeof(snum)){ memcpy(&snum, rp, sizeof(snum)); snum = TTNTOHS(snum); lua_pushnumber(lua, (uint16_t)snum); lua_rawseti(lua, 3, eidx++); rp += sizeof(snum); size -= sizeof(snum); } else { size = 0; } break; case 'N': if(size >= sizeof(inum)){ memcpy(&inum, rp, sizeof(inum)); inum = TTNTOHL(inum); lua_pushnumber(lua, (uint32_t)inum); lua_rawseti(lua, 3, eidx++); rp += sizeof(inum); size -= sizeof(inum); } else { size = 0; } break; case 'M': if(size >= sizeof(lnum)){ memcpy(&lnum, rp, sizeof(lnum)); lnum = TTNTOHLL(lnum); lua_pushnumber(lua, (uint64_t)lnum); lua_rawseti(lua, 3, eidx++); rp += sizeof(lnum); size -= sizeof(lnum); } else { size = 0; } break; case 'w': case 'W': wnum = 0; do { inum = *(unsigned char *)rp; wnum = wnum * 0x80 + (inum & 0x7f); rp++; size--; } while(inum >= 0x80 && size > 0); lua_pushnumber(lua, wnum); lua_rawseti(lua, 3, eidx++); break; } } format++; if(size < 1) break; } lua_replace(lua, 1); lua_settop(lua, 1); return 1; } /* for _split function */ static int serv_split(lua_State *lua){ int argc = lua_gettop(lua); if(argc < 1){ lua_pushstring(lua, "_split: invalid arguments"); lua_error(lua); } size_t isiz; const char *ibuf = lua_tolstring(lua, 1, &isiz); if(!ibuf){ lua_pushstring(lua, "_split: invalid arguments"); lua_error(lua); } const char *delims = argc > 1 ? lua_tostring(lua, 2) : NULL; lua_newtable(lua); int lnum = 1; if(delims){ const char *str = ibuf; while(true){ const char *sp = str; while(*str != '\0' && !strchr(delims, *str)){ str++; } lua_pushlstring(lua, sp, str - sp); lua_rawseti(lua, -2, lnum++); if(*str == '\0') break; str++; } } else { const char *ptr = ibuf; int size = isiz; while(size >= 0){ const char *rp = ptr; const char *ep = ptr + size; while(rp < ep){ if(*rp == '\0') break; rp++; } lua_pushlstring(lua, ptr, rp - ptr); lua_rawseti(lua, -2, lnum++); rp++; size -= rp - ptr; ptr = rp; } } lua_replace(lua, 1); lua_settop(lua, 1); return 1; } /* for _codec function */ static int serv_codec(lua_State *lua){ int argc = lua_gettop(lua); if(argc != 2){ lua_pushstring(lua, "_codec: invalid arguments"); lua_error(lua); } const char *mode = lua_tostring(lua, 1); size_t isiz; const char *ibuf = lua_tolstring(lua, 2, &isiz); if(!mode || !ibuf){ lua_pushstring(lua, "_codec: invalid arguments"); lua_error(lua); } char *obuf = NULL; int osiz = 0; if(*mode == '~'){ mode++; if(!tcstricmp(mode, "url")){ obuf = tcurldecode(ibuf, &osiz); } else if(!tcstricmp(mode, "base")){ obuf = tcbasedecode(ibuf, &osiz); } else if(!tcstricmp(mode, "quote")){ obuf = tcquotedecode(ibuf, &osiz); } else if(!tcstricmp(mode, "hex")){ obuf = tchexdecode(ibuf, &osiz); } else if(!tcstricmp(mode, "pack")){ obuf = tcpackdecode(ibuf, isiz, &osiz); } else if(!tcstricmp(mode, "tcbs")){ obuf = tcbsdecode(ibuf, isiz, &osiz); } else if(!tcstricmp(mode, "deflate")){ obuf = tcinflate(ibuf, isiz, &osiz); } else if(!tcstricmp(mode, "gzip")){ obuf = tcgzipdecode(ibuf, isiz, &osiz); } else if(!tcstricmp(mode, "bzip")){ obuf = tcbzipdecode(ibuf, isiz, &osiz); } else if(!tcstricmp(mode, "xml")){ obuf = tcxmlunescape(ibuf); osiz = obuf ? strlen(obuf) : 0; } } else { if(!tcstricmp(mode, "url")){ obuf = tcurlencode(ibuf, isiz); osiz = obuf ? strlen(obuf) : 0; } else if(!tcstricmp(mode, "base")){ obuf = tcbaseencode(ibuf, isiz); osiz = obuf ? strlen(obuf) : 0; } else if(!tcstricmp(mode, "quote")){ obuf = tcquoteencode(ibuf, isiz); osiz = obuf ? strlen(obuf) : 0; } else if(!tcstricmp(mode, "hex")){ obuf = tchexencode(ibuf, isiz); osiz = obuf ? strlen(obuf) : 0; } else if(!tcstricmp(mode, "pack")){ obuf = tcpackencode(ibuf, isiz, &osiz); } else if(!tcstricmp(mode, "tcbs")){ obuf = tcbsencode(ibuf, isiz, &osiz); } else if(!tcstricmp(mode, "deflate")){ obuf = tcdeflate(ibuf, isiz, &osiz); } else if(!tcstricmp(mode, "gzip")){ obuf = tcgzipencode(ibuf, isiz, &osiz); } else if(!tcstricmp(mode, "bzip")){ obuf = tcbzipencode(ibuf, isiz, &osiz); } else if(!tcstricmp(mode, "xml")){ obuf = tcxmlescape(ibuf); osiz = obuf ? strlen(obuf) : 0; } } lua_settop(lua, 0); if(obuf){ lua_pushlstring(lua, obuf, osiz); tcfree(obuf); } else { lua_pushnil(lua); } return 1; } /* for _hash function */ static int serv_hash(lua_State *lua){ int argc = lua_gettop(lua); if(argc != 2){ lua_pushstring(lua, "_hash: invalid arguments"); lua_error(lua); } const char *mode = lua_tostring(lua, 1); size_t isiz; const char *ibuf = lua_tolstring(lua, 2, &isiz); if(!mode || !ibuf){ lua_pushstring(lua, "_hash: invalid arguments"); lua_error(lua); } if(!tcstricmp(mode, "md5")){ char obuf[48]; tcmd5hash(ibuf, isiz, obuf); lua_settop(lua, 0); lua_pushstring(lua, obuf); } else if(!tcstricmp(mode, "md5raw")){ char obuf[48]; tcmd5hash(ibuf, isiz, obuf); int esiz; char *ebuf = tchexdecode(obuf, &esiz); lua_settop(lua, 0); lua_pushlstring(lua, ebuf, esiz); tcfree(ebuf); } else if(!tcstricmp(mode, "crc32")){ uint32_t crc = tcgetcrc(ibuf, isiz); lua_settop(lua, 0); lua_pushnumber(lua, crc); } else { lua_settop(lua, 0); lua_pushnil(lua); } return 1; } /* for _bit function */ static int serv_bit(lua_State *lua){ int argc = lua_gettop(lua); if(argc < 2){ lua_pushstring(lua, "_bit: invalid arguments"); lua_error(lua); } const char *mode = lua_tostring(lua, 1); uint32_t num = lua_tonumber(lua, 2); uint32_t aux = argc > 2 ? lua_tonumber(lua, 3) : 0; if(!mode){ lua_pushstring(lua, "_bit: invalid arguments"); lua_error(lua); } else if(!tcstricmp(mode, "and")){ num &= aux; } else if(!tcstricmp(mode, "or")){ num |= aux; } else if(!tcstricmp(mode, "xor")){ num ^= aux; } else if(!tcstricmp(mode, "not")){ num = ~num; } else if(!tcstricmp(mode, "left")){ num <<= aux; } else if(!tcstricmp(mode, "right")){ num >>= aux; } else { lua_pushstring(lua, "_bit: invalid arguments"); lua_error(lua); } lua_settop(lua, 0); lua_pushnumber(lua, num); return 1; } /* for _strstr function */ static int serv_strstr(lua_State *lua){ int argc = lua_gettop(lua); if(argc < 2){ lua_pushstring(lua, "_strstr: invalid arguments"); lua_error(lua); } const char *str = lua_tostring(lua, 1); const char *pat = lua_tostring(lua, 2); if(!str || !pat){ lua_pushstring(lua, "_strstr: invalid arguments"); lua_error(lua); } const char *alt = argc > 2 ? lua_tostring(lua, 3) : NULL; if(alt){ TCXSTR *xstr = tcxstrnew(); int plen = strlen(pat); int alen = strlen(alt); if(plen > 0){ char *pv; while((pv = strstr(str, pat)) != NULL){ tcxstrcat(xstr, str, pv - str); tcxstrcat(xstr, alt, alen); str = pv + plen; } } tcxstrcat2(xstr, str); lua_settop(lua, 0); lua_pushstring(lua, tcxstrptr(xstr)); tcxstrdel(xstr); } else { char *pv = strstr(str, pat); if(pv){ int idx = pv - str + 1; lua_settop(lua, 0); lua_pushinteger(lua, idx); } else { lua_settop(lua, 0); lua_pushinteger(lua, 0); } } return 1; } /* for _regex function */ static int serv_regex(lua_State *lua){ int argc = lua_gettop(lua); if(argc < 2){ lua_pushstring(lua, "_regex: invalid arguments"); lua_error(lua); } const char *str = lua_tostring(lua, 1); const char *regex = lua_tostring(lua, 2); if(!str || !regex){ lua_pushstring(lua, "_regex: invalid arguments"); lua_error(lua); } const char *alt = argc > 2 ? lua_tostring(lua, 3) : NULL; if(alt){ char *res = tcregexreplace(str, regex, alt); lua_settop(lua, 0); lua_pushstring(lua, res); tcfree(res); } else { if(tcregexmatch(str, regex)){ lua_settop(lua, 0); lua_pushboolean(lua, true); } else { lua_settop(lua, 0); lua_pushboolean(lua, false); } } return 1; } /* for _ucs function */ static int serv_ucs(lua_State *lua){ int argc = lua_gettop(lua); if(argc != 1){ lua_pushstring(lua, "ucs: invalid arguments"); lua_error(lua); } if(lua_type(lua, 1) == LUA_TTABLE){ int anum = lua_objlen(lua, 1); uint16_t *ary = tcmalloc(sizeof(*ary) * anum + 1); for(int i = 1; i <= anum; i++){ lua_rawgeti(lua, 1, i); ary[i-1] = lua_tointeger(lua, 2); lua_pop(lua, 1); } char *str = tcmalloc(anum * 3 + 1); tcstrucstoutf(ary, anum, str); lua_settop(lua, 0); lua_pushstring(lua, str); tcfree(str); tcfree(ary); } else { size_t len; const char *str = lua_tolstring(lua, 1, &len); if(!str){ lua_pushstring(lua, "ucs: invalid arguments"); lua_error(lua); } uint16_t *ary = tcmalloc(sizeof(*ary) * len + 1); int anum; tcstrutftoucs(str, ary, &anum); lua_settop(lua, 0); lua_createtable(lua, anum, 0); for(int i = 0; i < anum; i++){ lua_pushinteger(lua, ary[i]); lua_rawseti(lua, 1, i + 1); } tcfree(ary); } return 1; } /* for _dist function */ static int serv_dist(lua_State *lua){ int argc = lua_gettop(lua); if(argc < 2){ lua_pushstring(lua, "dist: invalid arguments"); lua_error(lua); } const char *astr = lua_tostring(lua, 1); const char *bstr = lua_tostring(lua, 2); bool utf = argc > 2 ? lua_toboolean(lua, 3) : false; if(!astr || !bstr){ lua_pushstring(lua, "dist: invalid arguments"); lua_error(lua); } int rv = utf ? tcstrdistutf(astr, bstr) : tcstrdist(astr, bstr); lua_settop(lua, 0); lua_pushnumber(lua, rv); return 1; } /* for _isect function */ static int serv_isect(lua_State *lua){ int argc = lua_gettop(lua); if(argc == 1 && lua_type(lua, 1) == LUA_TTABLE){ int len = lua_objlen(lua, 1); for(int i = 1; i <= len; i++){ lua_rawgeti(lua, 1, i); if(lua_type(lua, -1) == LUA_TTABLE){ argc++; } else { lua_pop(lua, 1); break; } } if(argc > 1){ lua_remove(lua, 1); argc--; } } int tnum = 0; int rnum = 0; for(int i = 1; i <= argc; i++){ if(lua_type(lua, i) != LUA_TTABLE) continue; int len = lua_objlen(lua, i); if(len < 1){ lua_settop(lua, 0); lua_newtable(lua); return 1; } tnum++; rnum += len; } if(tnum == 2){ TCMAP *former = NULL; TCMAP *latter = NULL; for(int i = 1; i <= argc; i++){ if(lua_type(lua, i) != LUA_TTABLE) continue; int len = lua_objlen(lua, i); if(former){ latter = tcmapnew2(tclmin(len, tcmaprnum(former))); for(int j = 1; j <= len; j++){ lua_rawgeti(lua, i, j); size_t size; const char *ptr = lua_tolstring(lua, -1, &size); if(ptr){ int vsiz; if(tcmapget(former, ptr, size, &vsiz)) tcmapput(latter, ptr, size, "", 0); } lua_pop(lua, 1); } break; } else { former = tcmapnew2(len); for(int j = 1; j <= len; j++){ lua_rawgeti(lua, i, j); size_t size; const char *ptr = lua_tolstring(lua, -1, &size); if(ptr) tcmapput(former, ptr, size, "", 0); lua_pop(lua, 1); } } } lua_settop(lua, 0); if(latter){ lua_createtable(lua, (int)tcmaprnum(latter), 0); tcmapiterinit(latter); int ridx = 1; const char *kbuf; int ksiz; while((kbuf = tcmapiternext(latter, &ksiz)) != NULL){ lua_pushlstring(lua, kbuf, ksiz); lua_rawseti(lua, 1, ridx++); } tcmapdel(latter); } else { lua_newtable(lua); } if(former) tcmapdel(former); } else { TCMAP *freq = tcmapnew2(rnum); for(int i = 1; i <= argc; i++){ if(lua_type(lua, i) != LUA_TTABLE) continue; int len = lua_objlen(lua, i); TCMAP *uniq = tcmapnew2(len); for(int j = 1; j <= len; j++){ lua_rawgeti(lua, i, j); size_t size; const char *ptr = lua_tolstring(lua, -1, &size); if(ptr){ int vsiz; if(!tcmapget(uniq, ptr, size, &vsiz)){ tcmapaddint(freq, ptr, size, 1); tcmapput(uniq, ptr, size, "", 0); } } lua_pop(lua, 1); } tcmapdel(uniq); } lua_settop(lua, 0); lua_createtable(lua, (int)tcmaprnum(freq), 0); tcmapiterinit(freq); int ridx = 1; const char *kbuf; int ksiz; while((kbuf = tcmapiternext(freq, &ksiz)) != NULL){ int vsiz; const char *vbuf = tcmapiterval(kbuf, &vsiz); int num = *(int *)vbuf; if(num != tnum) continue; lua_pushlstring(lua, kbuf, ksiz); lua_rawseti(lua, 1, ridx++); } tcmapdel(freq); } return 1; } /* for _union function */ static int serv_union(lua_State *lua){ int argc = lua_gettop(lua); if(argc == 1 && lua_type(lua, 1) == LUA_TTABLE){ int len = lua_objlen(lua, 1); for(int i = 1; i <= len; i++){ lua_rawgeti(lua, 1, i); if(lua_type(lua, -1) == LUA_TTABLE){ argc++; } else { lua_pop(lua, 1); break; } } if(argc > 1){ lua_remove(lua, 1); argc--; } } int rnum = 0; for(int i = 1; i <= argc; i++){ if(lua_type(lua, i) != LUA_TTABLE) continue; rnum += lua_objlen(lua, i); } TCMAP *result = tcmapnew2(rnum); for(int i = 1; i <= argc; i++){ if(lua_type(lua, i) != LUA_TTABLE) continue; int len = lua_objlen(lua, i); for(int j = 1; j <= len; j++){ lua_rawgeti(lua, i, j); size_t size; const char *ptr = lua_tolstring(lua, -1, &size); if(ptr) tcmapput(result, ptr, size, "", 0); lua_pop(lua, 1); } } lua_settop(lua, 0); lua_createtable(lua, (int)tcmaprnum(result), 0); tcmapiterinit(result); int ridx = 1; const char *kbuf; int ksiz; while((kbuf = tcmapiternext(result, &ksiz)) != NULL){ lua_pushlstring(lua, kbuf, ksiz); lua_rawseti(lua, 1, ridx++); } tcmapdel(result); return 1; } /* for _time function */ static int serv_time(lua_State *lua){ int argc = lua_gettop(lua); if(argc != 0){ lua_pushstring(lua, "_time: invalid arguments"); lua_error(lua); } lua_settop(lua, 0); lua_pushnumber(lua, tctime()); return 1; } /* for _sleep function */ static int serv_sleep(lua_State *lua){ int argc = lua_gettop(lua); if(argc != 1){ lua_pushstring(lua, "_sleep: invalid arguments"); lua_error(lua); } double sec = lua_tonumber(lua, 1); if(!lua_isnumber(lua, 1)){ lua_pushstring(lua, "_sleep: invalid arguments"); lua_error(lua); } lua_settop(lua, 0); lua_pushboolean(lua, tcsleep(sec)); return 1; } /* for stat function */ static int serv_stat(lua_State *lua){ int argc = lua_gettop(lua); if(argc != 1){ lua_pushstring(lua, "_stat: invalid arguments"); lua_error(lua); } const char *path = lua_tostring(lua, 1); if(!path){ lua_pushstring(lua, "_stat: invalid arguments"); lua_error(lua); } struct stat sbuf; if(lstat(path, &sbuf) == 0){ lua_settop(lua, 0); lua_newtable(lua); lua_pushnumber(lua, sbuf.st_dev); lua_setfield(lua, -2, "dev"); lua_pushnumber(lua, sbuf.st_ino); lua_setfield(lua, -2, "ino"); lua_pushnumber(lua, sbuf.st_mode); lua_setfield(lua, -2, "mode"); lua_pushnumber(lua, sbuf.st_nlink); lua_setfield(lua, -2, "nlink"); lua_pushnumber(lua, sbuf.st_uid); lua_setfield(lua, -2, "uid"); lua_pushnumber(lua, sbuf.st_gid); lua_setfield(lua, -2, "gid"); lua_pushnumber(lua, sbuf.st_rdev); lua_setfield(lua, -2, "rdev"); lua_pushnumber(lua, sbuf.st_size); lua_setfield(lua, -2, "size"); lua_pushnumber(lua, sbuf.st_blksize); lua_setfield(lua, -2, "blksize"); lua_pushnumber(lua, sbuf.st_blocks); lua_setfield(lua, -2, "blocks"); lua_pushnumber(lua, sbuf.st_atime); lua_setfield(lua, -2, "atime"); lua_pushnumber(lua, sbuf.st_mtime); lua_setfield(lua, -2, "mtime"); lua_pushnumber(lua, sbuf.st_ctime); lua_setfield(lua, -2, "ctime"); lua_pushboolean(lua, S_ISREG(sbuf.st_mode)); lua_setfield(lua, -2, "_regular"); lua_pushboolean(lua, S_ISDIR(sbuf.st_mode)); lua_setfield(lua, -2, "_directory"); bool readable = false; bool writable = false; bool executable = false; if(sbuf.st_uid == geteuid()){ if(sbuf.st_mode & S_IRUSR) readable = true; if(sbuf.st_mode & S_IWUSR) writable = true; if(sbuf.st_mode & S_IXUSR) executable = true; } if(sbuf.st_gid == getegid()){ if(sbuf.st_mode & S_IRGRP) readable = true; if(sbuf.st_mode & S_IWGRP) writable = true; if(sbuf.st_mode & S_IXGRP) executable = true; } if(sbuf.st_mode & S_IROTH) readable = true; if(sbuf.st_mode & S_IWOTH) writable = true; if(sbuf.st_mode & S_IXOTH) executable = true; lua_pushboolean(lua, readable); lua_setfield(lua, -2, "_readable"); lua_pushboolean(lua, writable); lua_setfield(lua, -2, "_writable"); lua_pushboolean(lua, executable); lua_setfield(lua, -2, "_executable"); char *rpath = tcrealpath(path); if(rpath){ lua_pushstring(lua, rpath); lua_setfield(lua, -2, "_realpath"); tcfree(rpath); } } else { lua_settop(lua, 0); lua_pushnil(lua); } return 1; } /* for _glob function */ static int serv_glob(lua_State *lua){ int argc = lua_gettop(lua); if(argc != 1){ lua_pushstring(lua, "_glob: invalid arguments"); lua_error(lua); } const char *pattern = lua_tostring(lua, 1); if(!pattern){ lua_pushstring(lua, "_glob: invalid arguments"); lua_error(lua); } TCLIST *paths = tcglobpat(pattern); int pnum = tclistnum(paths); lua_settop(lua, 0); lua_createtable(lua, pnum, 0); for(int i = 0; i < pnum; i++){ int size; const char *buf = tclistval(paths, i, &size); lua_pushlstring(lua, buf, size); lua_rawseti(lua, -2, i + 1); } tclistdel(paths); return 1; } /* for _remove function */ static int serv_remove(lua_State *lua){ int argc = lua_gettop(lua); if(argc != 1){ lua_pushstring(lua, "_remove: invalid arguments"); lua_error(lua); } const char *path = lua_tostring(lua, 1); if(!path){ lua_pushstring(lua, "_remove: invalid arguments"); lua_error(lua); } bool rv = tcremovelink(path); lua_settop(lua, 0); lua_pushboolean(lua, rv); return 1; } /* for _mkdir function */ static int serv_mkdir(lua_State *lua){ int argc = lua_gettop(lua); if(argc != 1){ lua_pushstring(lua, "_mkdir: invalid arguments"); lua_error(lua); } const char *path = lua_tostring(lua, 1); if(!path){ lua_pushstring(lua, "_mkdir: invalid arguments"); lua_error(lua); } bool rv = mkdir(path, 00755) == 0; lua_settop(lua, 0); lua_pushboolean(lua, rv); return 1; } #endif // END OF FILE tokyotyrant-1.1.40/scrext.h000066400000000000000000000077121133351147200156740ustar00rootroot00000000000000/************************************************************************************************* * Scripting language extension of Tokyo Tyrant * Copyright (C) 2006-2010 Mikio Hirabayashi * This file is part of Tokyo Tyrant. * Tokyo Tyrant is free software; you can redistribute it and/or modify it under the terms of * the GNU Lesser General Public License as published by the Free Software Foundation; either * version 2.1 of the License or any later version. Tokyo Tyrant 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 Lesser General Public * License for more details. * You should have received a copy of the GNU Lesser General Public License along with Tokyo * Tyrant; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA. *************************************************************************************************/ #ifndef _SCREXT_H // duplication check #define _SCREXT_H /************************************************************************************************* * configuration *************************************************************************************************/ #include "myconf.h" #include #include #include #if defined(_MYLUA) #define TTLUAEXT #else #define TTNOEXT #endif /************************************************************************************************* * pseudo API *************************************************************************************************/ /* Initialize the scripting language extension. `screxts' specifies an array of the scripting language extension objects. `thnum' specifies the number of the native threads. `thid' specifies the thread ID number. `path' specifies the path of the initilizing script. `adb' specifies the abstract database object. `ulog' specifies the update log object. `sid' specifies the server ID. `stash' specifies the global stash object. `lock' specifies the global lock object. `logger' specifies the pointer to a function to do with a log message. `logopq' specifies the opaque pointer for the logging function. The return value is the scripting object or `NULL' on failure. */ void *scrextnew(void **screxts, int thnum, int thid, const char *path, TCADB *adb, TCULOG *ulog, uint32_t sid, TCMDB *stash, TCMDB *lock, void (*logger)(int, const char *, void *), void *logopq); /* Destroy the scripting language extension. `scr' specifies the scripting object. If successful, the return value is true, else, it is false. */ bool scrextdel(void *scr); /* Call a method of the scripting language extension. `scr' specifies the scripting object. `name' specifies the name of the method to be called. `kbuf' specifies the pointer to the region of the key. `ksiz' specifies the size of the region of the key. `vbuf' specifies the pointer to the region of the value. `vsiz' specifies the size of the region of the value. `sp' specifies the pointer to the variable into which the size of the region of the return value is assigned. If successful, the return value is the pointer to the region of the value of the corresponding record. It should be allocated by `malloc' call. `NULL' is returned on failure. Note that the returned region is released by the caller. */ char *scrextcallmethod(void *scr, const char *name, const void *kbuf, int ksiz, const void *vbuf, int vsiz, int *sp); /* Send the terminate signal to the scripting language extension. `scr' specifies the scripting object. If successful, the return value is true, else, it is false. */ bool scrextkill(void *scr); #endif // duplication check // END OF FILE tokyotyrant-1.1.40/tcrdb.c000066400000000000000000002345171133351147200154620ustar00rootroot00000000000000/************************************************************************************************* * The remote database API of Tokyo Tyrant * Copyright (C) 2006-2010 Mikio Hirabayashi * This file is part of Tokyo Tyrant. * Tokyo Tyrant is free software; you can redistribute it and/or modify it under the terms of * the GNU Lesser General Public License as published by the Free Software Foundation; either * version 2.1 of the License or any later version. Tokyo Tyrant 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 Lesser General Public * License for more details. * You should have received a copy of the GNU Lesser General Public License along with Tokyo * Tyrant; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA. *************************************************************************************************/ #include "tcutil.h" #include "tcadb.h" #include "ttutil.h" #include "tcrdb.h" #include "myconf.h" #define RDBRECONWAIT 0.1 // wait time to reconnect #define RDBNUMCOLMAX 16 // maximum number of columns of the long double typedef struct { // type of structure for a meta search query pthread_t tid; // thread ID number RDBQRY *qry; // query object TCLIST *res; // response object int max; // max number of retrieval int skip; // skipping number of retrieval } PARASEARCHARG; typedef struct { // type of structure for a sort record const char *cbuf; // pointer to the column buffer int csiz; // size of the column buffer char *obuf; // pointer to the sort key int osiz; // size of the sort key } RDBSORTREC; /* private function prototypes */ static bool tcrdblockmethod(TCRDB *rdb); static void tcrdbunlockmethod(TCRDB *rdb); static bool tcrdbreconnect(TCRDB *rdb); static bool tcrdbsend(TCRDB *rdb, const void *buf, int size); static bool tcrdbtuneimpl(TCRDB *rdb, double timeout, int opts); static bool tcrdbopenimpl(TCRDB *rdb, const char *host, int port); static bool tcrdbcloseimpl(TCRDB *rdb); static bool tcrdbputimpl(TCRDB *rdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz); static bool tcrdbputkeepimpl(TCRDB *rdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz); static bool tcrdbputcatimpl(TCRDB *rdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz); static bool tcrdbputshlimpl(TCRDB *rdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz, int width); static bool tcrdbputnrimpl(TCRDB *rdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz); static bool tcrdboutimpl(TCRDB *rdb, const void *kbuf, int ksiz); static void *tcrdbgetimpl(TCRDB *rdb, const void *kbuf, int ksiz, int *sp); static bool tcrdbmgetimpl(TCRDB *rdb, TCMAP *recs); static int tcrdbvsizimpl(TCRDB *rdb, const void *kbuf, int ksiz); static bool tcrdbiterinitimpl(TCRDB *rdb); static void *tcrdbiternextimpl(TCRDB *rdb, int *sp); static TCLIST *tcrdbfwmkeysimpl(TCRDB *rdb, const void *pbuf, int psiz, int max); static int tcrdbaddintimpl(TCRDB *rdb, const void *kbuf, int ksiz, int num); static double tcrdbadddoubleimpl(TCRDB *rdb, const void *kbuf, int ksiz, double num); static void *tcrdbextimpl(TCRDB *rdb, const char *name, int opts, const void *kbuf, int ksiz, const void *vbuf, int vsiz, int *sp); static bool tcrdbsyncimpl(TCRDB *rdb); static bool tcrdboptimizeimpl(TCRDB *rdb, const char *params); static bool tcrdbvanishimpl(TCRDB *rdb); static bool tcrdbcopyimpl(TCRDB *rdb, const char *path); static bool tcrdbrestoreimpl(TCRDB *rdb, const char *path, uint64_t ts, int opts); static bool tcrdbsetmstimpl(TCRDB *rdb, const char *host, int port, uint64_t ts, int opts); const char *tcrdbexprimpl(TCRDB *rdb); static uint64_t tcrdbrnumimpl(TCRDB *rdb); static uint64_t tcrdbsizeimpl(TCRDB *rdb); static char *tcrdbstatimpl(TCRDB *rdb); static TCLIST *tcrdbmiscimpl(TCRDB *rdb, const char *name, int opts, const TCLIST *args); static void tcrdbqrypopmeta(RDBQRY *qry, TCLIST *res); static void *tcrdbparasearchworker(PARASEARCHARG *arg); static long double tcrdbatof(const char *str); static int rdbcmpsortrecstrasc(const RDBSORTREC *a, const RDBSORTREC *b); static int rdbcmpsortrecstrdesc(const RDBSORTREC *a, const RDBSORTREC *b); static int rdbcmpsortrecnumasc(const RDBSORTREC *a, const RDBSORTREC *b); static int rdbcmpsortrecnumdesc(const RDBSORTREC *a, const RDBSORTREC *b); /************************************************************************************************* * API *************************************************************************************************/ /* Get the message string corresponding to an error code. */ const char *tcrdberrmsg(int ecode){ switch(ecode){ case TTESUCCESS: return "success"; case TTEINVALID: return "invalid operation"; case TTENOHOST: return "host not found"; case TTEREFUSED: return "connection refused"; case TTESEND: return "send error"; case TTERECV: return "recv error"; case TTEKEEP: return "existing record"; case TTENOREC: return "no record found"; case TTEMISC: return "miscellaneous error"; } return "unknown error"; } /* Create a remote database object. */ TCRDB *tcrdbnew(void){ TCRDB *rdb = tcmalloc(sizeof(*rdb)); if(pthread_mutex_init(&rdb->mmtx, NULL) != 0) tcmyfatal("pthread_mutex_init failed"); if(pthread_key_create(&rdb->eckey, NULL) != 0) tcmyfatal("pthread_key_create failed"); rdb->host = NULL; rdb->port = -1; rdb->expr = NULL; rdb->fd = -1; rdb->sock = NULL; rdb->timeout = UINT_MAX; rdb->opts = 0; tcrdbsetecode(rdb, TTESUCCESS); return rdb; } /* Delete a remote database object. */ void tcrdbdel(TCRDB *rdb){ assert(rdb); if(rdb->fd >= 0) tcrdbclose(rdb); if(rdb->expr) tcfree(rdb->expr); if(rdb->host) tcfree(rdb->host); pthread_key_delete(rdb->eckey); pthread_mutex_destroy(&rdb->mmtx); tcfree(rdb); } /* Get the last happened error code of a remote database object. */ int tcrdbecode(TCRDB *rdb){ assert(rdb); return (int)(intptr_t)pthread_getspecific(rdb->eckey); } /* Set the tuning parameters of a remote database object. */ bool tcrdbtune(TCRDB *rdb, double timeout, int opts){ assert(rdb); if(!tcrdblockmethod(rdb)) return false; bool rv; pthread_cleanup_push((void (*)(void *))tcrdbunlockmethod, rdb); rv = tcrdbtuneimpl(rdb, timeout, opts); pthread_cleanup_pop(1); return rv; } /* Open a remote database. */ bool tcrdbopen(TCRDB *rdb, const char *host, int port){ assert(rdb && host); if(!tcrdblockmethod(rdb)) return false; bool rv; pthread_cleanup_push((void (*)(void *))tcrdbunlockmethod, rdb); rv = tcrdbopenimpl(rdb, host, port); pthread_cleanup_pop(1); return rv; } /* Open a remote database with a simple server expression. */ bool tcrdbopen2(TCRDB *rdb, const char *expr){ assert(rdb && expr); bool err = false; int port; char *host = ttbreakservexpr(expr, &port); char *pv = strchr(expr, '#'); double tout = 0.0; if(pv){ TCLIST *elems = tcstrsplit(pv + 1, "#"); int ln = tclistnum(elems); for(int i = 0; i < ln; i++){ const char *elem = TCLISTVALPTR(elems, i); pv = strchr(elem, '='); if(!pv) continue; *(pv++) = '\0'; if(!tcstricmp(elem, "host") || !tcstricmp(elem, "name")){ tcfree(host); host = ttbreakservexpr(pv, NULL); } else if(!tcstricmp(elem, "port")){ port = tcatoi(pv); } else if(!tcstricmp(elem, "tout") || !tcstricmp(elem, "timeout")){ tout = tcatof(pv); } } tclistdel(elems); } if(tout > 0) tcrdbtune(rdb, tout, RDBTRECON); if(!tcrdbopen(rdb, host, port)) err = true; tcfree(host); return !err; } /* Close a remote database object. */ bool tcrdbclose(TCRDB *rdb){ assert(rdb); if(!tcrdblockmethod(rdb)) return false; bool rv; pthread_cleanup_push((void (*)(void *))tcrdbunlockmethod, rdb); rv = tcrdbcloseimpl(rdb); pthread_cleanup_pop(1); return rv; } /* Store a record into a remote database object. */ bool tcrdbput(TCRDB *rdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){ assert(rdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0); if(!tcrdblockmethod(rdb)) return false; bool rv; pthread_cleanup_push((void (*)(void *))tcrdbunlockmethod, rdb); rv = tcrdbputimpl(rdb, kbuf, ksiz, vbuf, vsiz); pthread_cleanup_pop(1); return rv; } /* Store a string record into a remote object. */ bool tcrdbput2(TCRDB *rdb, const char *kstr, const char *vstr){ assert(rdb && kstr && vstr); return tcrdbput(rdb, kstr, strlen(kstr), vstr, strlen(vstr)); } /* Store a new record into a remote database object. */ bool tcrdbputkeep(TCRDB *rdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){ assert(rdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0); if(!tcrdblockmethod(rdb)) return false; bool rv; pthread_cleanup_push((void (*)(void *))tcrdbunlockmethod, rdb); rv = tcrdbputkeepimpl(rdb, kbuf, ksiz, vbuf, vsiz); pthread_cleanup_pop(1); return rv; } /* Store a new string record into a remote database object. */ bool tcrdbputkeep2(TCRDB *rdb, const char *kstr, const char *vstr){ assert(rdb && kstr && vstr); return tcrdbputkeep(rdb, kstr, strlen(kstr), vstr, strlen(vstr)); } /* Concatenate a value at the end of the existing record in a remote database object. */ bool tcrdbputcat(TCRDB *rdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){ assert(rdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0); if(!tcrdblockmethod(rdb)) return false; bool rv; pthread_cleanup_push((void (*)(void *))tcrdbunlockmethod, rdb); rv = tcrdbputcatimpl(rdb, kbuf, ksiz, vbuf, vsiz); pthread_cleanup_pop(1); return rv; } /* Concatenate a string value at the end of the existing record in a remote database object. */ bool tcrdbputcat2(TCRDB *rdb, const char *kstr, const char *vstr){ assert(rdb && kstr && vstr); return tcrdbputcat(rdb, kstr, strlen(kstr), vstr, strlen(vstr)); } /* Concatenate a value at the end of the existing record and shift it to the left. */ bool tcrdbputshl(TCRDB *rdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz, int width){ assert(rdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0 && width >= 0); if(!tcrdblockmethod(rdb)) return false; bool rv; pthread_cleanup_push((void (*)(void *))tcrdbunlockmethod, rdb); rv = tcrdbputshlimpl(rdb, kbuf, ksiz, vbuf, vsiz, width); pthread_cleanup_pop(1); return rv; } /* Concatenate a string value at the end of the existing record and shift it to the left. */ bool tcrdbputshl2(TCRDB *rdb, const char *kstr, const char *vstr, int width){ assert(rdb && kstr && vstr); return tcrdbputshl(rdb, kstr, strlen(kstr), vstr, strlen(vstr), width); } /* Store a record into a remote database object without repsponse from the server. */ bool tcrdbputnr(TCRDB *rdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){ assert(rdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0); if(!tcrdblockmethod(rdb)) return false; bool rv; pthread_cleanup_push((void (*)(void *))tcrdbunlockmethod, rdb); rv = tcrdbputnrimpl(rdb, kbuf, ksiz, vbuf, vsiz); pthread_cleanup_pop(1); return rv; } /* Store a string record into a remote object without response from the server. */ bool tcrdbputnr2(TCRDB *rdb, const char *kstr, const char *vstr){ assert(rdb && kstr && vstr); return tcrdbputnr(rdb, kstr, strlen(kstr), vstr, strlen(vstr)); } /* Remove a record of a remote database object. */ bool tcrdbout(TCRDB *rdb, const void *kbuf, int ksiz){ assert(rdb && kbuf && ksiz >= 0); if(!tcrdblockmethod(rdb)) return false; bool rv; pthread_cleanup_push((void (*)(void *))tcrdbunlockmethod, rdb); rv = tcrdboutimpl(rdb, kbuf, ksiz); pthread_cleanup_pop(1); return rv; } /* Remove a string record of a remote database object. */ bool tcrdbout2(TCRDB *rdb, const char *kstr){ assert(rdb && kstr); return tcrdbout(rdb, kstr, strlen(kstr)); } /* Retrieve a record in a remote database object. */ void *tcrdbget(TCRDB *rdb, const void *kbuf, int ksiz, int *sp){ assert(rdb && kbuf && ksiz >= 0 && sp); if(!tcrdblockmethod(rdb)) return NULL; void *rv; pthread_cleanup_push((void (*)(void *))tcrdbunlockmethod, rdb); rv = tcrdbgetimpl(rdb, kbuf, ksiz, sp); pthread_cleanup_pop(1); return rv; } /* Retrieve a string record in a remote database object. */ char *tcrdbget2(TCRDB *rdb, const char *kstr){ assert(rdb && kstr); int vsiz; return tcrdbget(rdb, kstr, strlen(kstr), &vsiz); } /* Retrieve records in a remote database object. */ bool tcrdbget3(TCRDB *rdb, TCMAP *recs){ assert(rdb && recs); if(!tcrdblockmethod(rdb)) return false; bool rv; pthread_cleanup_push((void (*)(void *))tcrdbunlockmethod, rdb); rv = tcrdbmgetimpl(rdb, recs); pthread_cleanup_pop(1); return rv; } /* Get the size of the value of a record in a remote database object. */ int tcrdbvsiz(TCRDB *rdb, const void *kbuf, int ksiz){ assert(rdb && kbuf && ksiz >= 0); if(!tcrdblockmethod(rdb)) return -1; int rv; pthread_cleanup_push((void (*)(void *))tcrdbunlockmethod, rdb); rv = tcrdbvsizimpl(rdb, kbuf, ksiz); pthread_cleanup_pop(1); return rv; } /* Get the size of the value of a string record in a remote database object. */ int tcrdbvsiz2(TCRDB *rdb, const char *kstr){ assert(rdb && kstr); return tcrdbvsiz(rdb, kstr, strlen(kstr)); } /* Initialize the iterator of a remote database object. */ bool tcrdbiterinit(TCRDB *rdb){ assert(rdb); if(!tcrdblockmethod(rdb)) return false; bool rv; pthread_cleanup_push((void (*)(void *))tcrdbunlockmethod, rdb); rv = tcrdbiterinitimpl(rdb); pthread_cleanup_pop(1); return rv; } /* Get the next key of the iterator of a remote database object. */ void *tcrdbiternext(TCRDB *rdb, int *sp){ assert(rdb && sp); if(!tcrdblockmethod(rdb)) return NULL; void *rv; pthread_cleanup_push((void (*)(void *))tcrdbunlockmethod, rdb); rv = tcrdbiternextimpl(rdb, sp); pthread_cleanup_pop(1); return rv; } /* Get the next key string of the iterator of a remote database object. */ char *tcrdbiternext2(TCRDB *rdb){ assert(rdb); int vsiz; return tcrdbiternext(rdb, &vsiz); } /* Get forward matching keys in a remote database object. */ TCLIST *tcrdbfwmkeys(TCRDB *rdb, const void *pbuf, int psiz, int max){ assert(rdb && pbuf && psiz >= 0); if(!tcrdblockmethod(rdb)) return tclistnew2(1); TCLIST *rv; pthread_cleanup_push((void (*)(void *))tcrdbunlockmethod, rdb); rv = tcrdbfwmkeysimpl(rdb, pbuf, psiz, max); pthread_cleanup_pop(1); return rv; } /* Get forward matching string keys in a remote database object. */ TCLIST *tcrdbfwmkeys2(TCRDB *rdb, const char *pstr, int max){ assert(rdb && pstr); return tcrdbfwmkeys(rdb, pstr, strlen(pstr), max); } /* Add an integer to a record in a remote database object. */ int tcrdbaddint(TCRDB *rdb, const void *kbuf, int ksiz, int num){ assert(rdb && kbuf && ksiz >= 0); if(!tcrdblockmethod(rdb)) return INT_MIN; int rv; pthread_cleanup_push((void (*)(void *))tcrdbunlockmethod, rdb); rv = tcrdbaddintimpl(rdb, kbuf, ksiz, num); pthread_cleanup_pop(1); return rv; } /* Add a real number to a record in a remote database object. */ double tcrdbadddouble(TCRDB *rdb, const void *kbuf, int ksiz, double num){ assert(rdb && kbuf && ksiz >= 0); if(!tcrdblockmethod(rdb)) return nan(""); double rv; pthread_cleanup_push((void (*)(void *))tcrdbunlockmethod, rdb); rv = tcrdbadddoubleimpl(rdb, kbuf, ksiz, num); pthread_cleanup_pop(1); return rv; } /* Call a function of the scripting language extension. */ void *tcrdbext(TCRDB *rdb, const char *name, int opts, const void *kbuf, int ksiz, const void *vbuf, int vsiz, int *sp){ assert(rdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0); if(!tcrdblockmethod(rdb)) return NULL; void *rv; pthread_cleanup_push((void (*)(void *))tcrdbunlockmethod, rdb); rv = tcrdbextimpl(rdb, name, opts, kbuf, ksiz, vbuf, vsiz, sp); pthread_cleanup_pop(1); return rv; } /* Call a function of the scripting language extension with string parameters. */ char *tcrdbext2(TCRDB *rdb, const char *name, int opts, const char *kstr, const char *vstr){ assert(rdb && name && kstr && vstr); int vsiz; return tcrdbext(rdb, name, opts, kstr, strlen(kstr), vstr, strlen(vstr), &vsiz); } /* Synchronize updated contents of a remote database object with the file and the device. */ bool tcrdbsync(TCRDB *rdb){ assert(rdb); if(!tcrdblockmethod(rdb)) return false; bool rv; pthread_cleanup_push((void (*)(void *))tcrdbunlockmethod, rdb); rv = tcrdbsyncimpl(rdb); pthread_cleanup_pop(1); return rv; } /* Optimize the storage of a remove database object. */ bool tcrdboptimize(TCRDB *rdb, const char *params){ assert(rdb); if(!tcrdblockmethod(rdb)) return false; bool rv; pthread_cleanup_push((void (*)(void *))tcrdbunlockmethod, rdb); rv = tcrdboptimizeimpl(rdb, params); pthread_cleanup_pop(1); return rv; } /* Remove all records of a remote database object. */ bool tcrdbvanish(TCRDB *rdb){ assert(rdb); if(!tcrdblockmethod(rdb)) return false; bool rv; pthread_cleanup_push((void (*)(void *))tcrdbunlockmethod, rdb); rv = tcrdbvanishimpl(rdb); pthread_cleanup_pop(1); return rv; } /* Copy the database file of a remote database object. */ bool tcrdbcopy(TCRDB *rdb, const char *path){ assert(rdb && path); if(!tcrdblockmethod(rdb)) return false; bool rv; pthread_cleanup_push((void (*)(void *))tcrdbunlockmethod, rdb); rv = tcrdbcopyimpl(rdb, path); pthread_cleanup_pop(1); return rv; } /* Restore the database file of a remote database object from the update log. */ bool tcrdbrestore(TCRDB *rdb, const char *path, uint64_t ts, int opts){ assert(rdb && path); if(!tcrdblockmethod(rdb)) return false; bool rv; pthread_cleanup_push((void (*)(void *))tcrdbunlockmethod, rdb); rv = tcrdbrestoreimpl(rdb, path, ts, opts); pthread_cleanup_pop(1); return rv; } /* Set the replication master of a remote database object from the update log. */ bool tcrdbsetmst(TCRDB *rdb, const char *host, int port, uint64_t ts, int opts){ assert(rdb); if(!tcrdblockmethod(rdb)) return false; bool rv; pthread_cleanup_push((void (*)(void *))tcrdbunlockmethod, rdb); rv = tcrdbsetmstimpl(rdb, host, port, ts, opts); pthread_cleanup_pop(1); return rv; } /* Set the replication master of a remote database object with a simple server expression. */ bool tcrdbsetmst2(TCRDB *rdb, const char *expr, uint64_t ts, int opts){ assert(rdb && expr); bool err = false; int port; char *host = ttbreakservexpr(expr, &port); if(!tcrdbsetmst(rdb, host, port, ts, opts)) err = true; tcfree(host); return !err; } /* Get the simple server expression of an abstract database object. */ const char *tcrdbexpr(TCRDB *rdb){ assert(rdb); if(!tcrdblockmethod(rdb)) return NULL; const char *rv; pthread_cleanup_push((void (*)(void *))tcrdbunlockmethod, rdb); rv = tcrdbexprimpl(rdb); pthread_cleanup_pop(1); return rv; } /* Get the number of records of a remote database object. */ uint64_t tcrdbrnum(TCRDB *rdb){ assert(rdb); if(!tcrdblockmethod(rdb)) return 0; uint64_t rv; pthread_cleanup_push((void (*)(void *))tcrdbunlockmethod, rdb); rv = tcrdbrnumimpl(rdb); pthread_cleanup_pop(1); return rv; } /* Get the size of the database of a remote database object. */ uint64_t tcrdbsize(TCRDB *rdb){ assert(rdb); if(!tcrdblockmethod(rdb)) return 0; uint64_t rv; pthread_cleanup_push((void (*)(void *))tcrdbunlockmethod, rdb); rv = tcrdbsizeimpl(rdb); pthread_cleanup_pop(1); return rv; } /* Get the status string of the database of a remote database object. */ char *tcrdbstat(TCRDB *rdb){ assert(rdb); if(!tcrdblockmethod(rdb)) return NULL; char *rv; pthread_cleanup_push((void (*)(void *))tcrdbunlockmethod, rdb); rv = tcrdbstatimpl(rdb); pthread_cleanup_pop(1); return rv; } /* Call a versatile function for miscellaneous operations of a remote database object. */ TCLIST *tcrdbmisc(TCRDB *rdb, const char *name, int opts, const TCLIST *args){ assert(rdb && name && args); if(!tcrdblockmethod(rdb)) return NULL; TCLIST *rv; pthread_cleanup_push((void (*)(void *))tcrdbunlockmethod, rdb); rv = tcrdbmiscimpl(rdb, name, opts, args); pthread_cleanup_pop(1); return rv; } /************************************************************************************************* * table extension *************************************************************************************************/ /* Store a record into a remote database object. */ bool tcrdbtblput(TCRDB *rdb, const void *pkbuf, int pksiz, TCMAP *cols){ assert(rdb && pkbuf && pksiz >= 0 && cols); TCLIST *args = tclistnew2(tcmaprnum(cols) * 2 + 1); tclistpush(args, pkbuf, pksiz); tcmapiterinit(cols); const char *kbuf; int ksiz; while((kbuf = tcmapiternext(cols, &ksiz)) != NULL){ int vsiz; const char *vbuf = tcmapiterval(kbuf, &vsiz); tclistpush(args, kbuf, ksiz); tclistpush(args, vbuf, vsiz); } TCLIST *rv = tcrdbmisc(rdb, "put", 0, args); tclistdel(args); if(!rv) return false; tclistdel(rv); return true; } /* Store a new record into a remote database object. */ bool tcrdbtblputkeep(TCRDB *rdb, const void *pkbuf, int pksiz, TCMAP *cols){ assert(rdb && pkbuf && pksiz >= 0 && cols); TCLIST *args = tclistnew2(tcmaprnum(cols) * 2 + 1); tclistpush(args, pkbuf, pksiz); tcmapiterinit(cols); const char *kbuf; int ksiz; while((kbuf = tcmapiternext(cols, &ksiz)) != NULL){ int vsiz; const char *vbuf = tcmapiterval(kbuf, &vsiz); tclistpush(args, kbuf, ksiz); tclistpush(args, vbuf, vsiz); } TCLIST *rv = tcrdbmisc(rdb, "putkeep", 0, args); tclistdel(args); if(!rv){ if(tcrdbecode(rdb) == TTEMISC) tcrdbsetecode(rdb, TTEKEEP); return false; } tclistdel(rv); return true; } /* Concatenate columns of the existing record in a remote database object. */ bool tcrdbtblputcat(TCRDB *rdb, const void *pkbuf, int pksiz, TCMAP *cols){ assert(rdb && pkbuf && pksiz >= 0 && cols); TCLIST *args = tclistnew2(tcmaprnum(cols) * 2 + 1); tclistpush(args, pkbuf, pksiz); tcmapiterinit(cols); const char *kbuf; int ksiz; while((kbuf = tcmapiternext(cols, &ksiz)) != NULL){ int vsiz; const char *vbuf = tcmapiterval(kbuf, &vsiz); tclistpush(args, kbuf, ksiz); tclistpush(args, vbuf, vsiz); } TCLIST *rv = tcrdbmisc(rdb, "putcat", 0, args); tclistdel(args); if(!rv) return false; tclistdel(rv); return true; } /* Remove a record of a remote database object. */ bool tcrdbtblout(TCRDB *rdb, const void *pkbuf, int pksiz){ assert(rdb && pkbuf && pksiz >= 0); TCLIST *args = tclistnew2(1); tclistpush(args, pkbuf, pksiz); TCLIST *rv = tcrdbmisc(rdb, "out", 0, args); tclistdel(args); if(!rv){ if(tcrdbecode(rdb) == TTEMISC) tcrdbsetecode(rdb, TTENOREC); return false; } tclistdel(rv); return true; } /* Retrieve a record in a remote database object. */ TCMAP *tcrdbtblget(TCRDB *rdb, const void *pkbuf, int pksiz){ assert(rdb && pkbuf && pksiz >= 0); TCLIST *args = tclistnew2(1); tclistpush(args, pkbuf, pksiz); TCLIST *rv = tcrdbmisc(rdb, "get", RDBMONOULOG, args); tclistdel(args); if(!rv){ if(tcrdbecode(rdb) == TTEMISC) tcrdbsetecode(rdb, TTENOREC); return NULL; } int num = tclistnum(rv); TCMAP *cols = tcmapnew2(num / 2 + 1); num--; for(int i = 0; i < num; i += 2){ int ksiz; const char *kbuf = tclistval(rv, i, &ksiz); int vsiz; const char *vbuf = tclistval(rv, i + 1, &vsiz); tcmapput(cols, kbuf, ksiz, vbuf, vsiz); } tclistdel(rv); return cols; } /* Set a column index to a remote database object. */ bool tcrdbtblsetindex(TCRDB *rdb, const char *name, int type){ assert(rdb && name); TCLIST *args = tclistnew2(2); tclistpush2(args, name); char typestr[TTNUMBUFSIZ]; sprintf(typestr, "%d", type); tclistpush2(args, typestr); TCLIST *rv = tcrdbmisc(rdb, "setindex", 0, args); tclistdel(args); if(!rv) return false; tclistdel(rv); return true; } /* Generate a unique ID number of a remote database object. */ int64_t tcrdbtblgenuid(TCRDB *rdb){ assert(rdb); TCLIST *args = tclistnew2(1); TCLIST *rv = tcrdbmisc(rdb, "genuid", 0, args); tclistdel(args); if(!rv) return -1; int64_t uid = -1; if(tclistnum(rv) > 0) uid = tcatoi(tclistval2(rv, 0)); tclistdel(rv); return uid; } /* Create a query object. */ RDBQRY *tcrdbqrynew(TCRDB *rdb){ assert(rdb); RDBQRY *qry = tcmalloc(sizeof(*qry)); qry->rdb = rdb; qry->args = tclistnew(); qry->hint = tcxstrnew(); tclistpush(qry->args, "hint", 4); return qry; } /* Delete a query object. */ void tcrdbqrydel(RDBQRY *qry){ assert(qry); tcxstrdel(qry->hint); tclistdel(qry->args); tcfree(qry); } /* Add a narrowing condition to a query object. */ void tcrdbqryaddcond(RDBQRY *qry, const char *name, int op, const char *expr){ assert(qry && name && expr); TCXSTR *xstr = tcxstrnew(); tcxstrcat2(xstr, "addcond"); tcxstrcat(xstr, "\0", 1); tcxstrcat2(xstr, name); tcxstrcat(xstr, "\0", 1); tcxstrprintf(xstr, "%d", op); tcxstrcat(xstr, "\0", 1); tcxstrcat2(xstr, expr); tclistpush(qry->args, tcxstrptr(xstr), tcxstrsize(xstr)); tcxstrdel(xstr); } /* Set the order of a query object. */ void tcrdbqrysetorder(RDBQRY *qry, const char *name, int type){ assert(qry && name); TCXSTR *xstr = tcxstrnew(); tcxstrcat2(xstr, "setorder"); tcxstrcat(xstr, "\0", 1); tcxstrcat2(xstr, name); tcxstrcat(xstr, "\0", 1); tcxstrprintf(xstr, "%d", type); tclistpush(qry->args, tcxstrptr(xstr), tcxstrsize(xstr)); tcxstrdel(xstr); } /* Set the limit number of records of the result of a query object. */ void tcrdbqrysetlimit(RDBQRY *qry, int max, int skip){ TCXSTR *xstr = tcxstrnew(); tcxstrcat2(xstr, "setlimit"); tcxstrcat(xstr, "\0", 1); tcxstrprintf(xstr, "%d", max); tcxstrcat(xstr, "\0", 1); tcxstrprintf(xstr, "%d", skip); tclistpush(qry->args, tcxstrptr(xstr), tcxstrsize(xstr)); tcxstrdel(xstr); } /* Execute the search of a query object. */ TCLIST *tcrdbqrysearch(RDBQRY *qry){ assert(qry); tcxstrclear(qry->hint); TCLIST *rv = tcrdbmisc(qry->rdb, "search", RDBMONOULOG, qry->args); if(!rv) return tclistnew2(1); tcrdbqrypopmeta(qry, rv); return rv; } /* Remove each record corresponding to a query object. */ bool tcrdbqrysearchout(RDBQRY *qry){ assert(qry); TCLIST *args = tclistdup(qry->args); tclistpush2(args, "out"); tcxstrclear(qry->hint); TCLIST *rv = tcrdbmisc(qry->rdb, "search", 0, args); tclistdel(args); if(!rv) return false; tcrdbqrypopmeta(qry, rv); tclistdel(rv); return true; } /* Get records corresponding to the search of a query object. */ TCLIST *tcrdbqrysearchget(RDBQRY *qry){ assert(qry); TCLIST *args = tclistdup(qry->args); tclistpush2(args, "get"); tcxstrclear(qry->hint); TCLIST *rv = tcrdbmisc(qry->rdb, "search", RDBMONOULOG, args); tclistdel(args); if(!rv) return tclistnew2(1); tcrdbqrypopmeta(qry, rv); return rv; } /* Get columns of a record in a search result. */ TCMAP *tcrdbqryrescols(TCLIST *res, int index){ assert(res && index >= 0); if(index >= tclistnum(res)) return NULL; int csiz; const char *cbuf = tclistval(res, index, &csiz); return tcstrsplit4(cbuf, csiz); } /* Get the count of corresponding records of a query object. */ int tcrdbqrysearchcount(RDBQRY *qry){ assert(qry); TCLIST *args = tclistdup(qry->args); tclistpush2(args, "count"); tcxstrclear(qry->hint); TCLIST *rv = tcrdbmisc(qry->rdb, "search", RDBMONOULOG, args); tclistdel(args); if(!rv) return 0; tcrdbqrypopmeta(qry, rv); int count = tclistnum(rv) > 0 ? tcatoi(tclistval2(rv, 0)) : 0; tclistdel(rv); return count; } /* Get the hint string of a query object. */ const char *tcrdbqryhint(RDBQRY *qry){ assert(qry); return tcxstrptr(qry->hint); } /* Retrieve records with multiple query objects and get the set of the result. */ TCLIST *tcrdbmetasearch(RDBQRY **qrys, int num, int type){ assert(qrys && num >= 0); if(num < 1) return tclistnew2(1); if(num < 2) return tcrdbqrysearch(qrys[0]); RDBQRY *qry = qrys[0]; TCLIST *args = tclistdup(qry->args); for(int i = 1; i < num; i++){ tclistpush(args, "next", 4); const TCLIST *targs = qrys[i]->args; int tanum = tclistnum(targs); for(int j = 0; j < tanum; j++){ int vsiz; const char *vbuf = tclistval(targs, j, &vsiz); tclistpush(args, vbuf, vsiz); } } char buf[TTNUMBUFSIZ]; int len = sprintf(buf, "mstype"); len += 1 + sprintf(buf + len + 1, "%d", type); tclistpush(args, buf, len); tcxstrclear(qry->hint); TCLIST *rv = tcrdbmisc(qry->rdb, "metasearch", RDBMONOULOG, args); tclistdel(args); if(!rv) rv = tclistnew2(1); tcrdbqrypopmeta(qrys[0], rv); return rv; } /* Search for multiple servers in parallel. */ TCLIST *tcrdbparasearch(RDBQRY **qrys, int num){ assert(qrys && num >= 0); if(num < 1) return tclistnew2(1); if(num < 2) return tcrdbqrysearchget(qrys[0]); int ocs = PTHREAD_CANCEL_DISABLE; pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &ocs); TCLIST *oargs = qrys[0]->args; char *oname = NULL; int otype = 0; int max = INT_MAX / 2; int skip = 0; for(int i = 0; i < tclistnum(oargs); i++){ int osiz; const char *obuf = tclistval(oargs, i, &osiz); if(!strcmp(obuf, "setlimit")){ TCLIST *elems = tcstrsplit2(obuf, osiz); if(tclistnum(elems) > 1) max = tcatoi(tclistval2(elems, 1)); if(tclistnum(elems) > 2) skip = tcatoi(tclistval2(elems, 2)); tclistdel(elems); } else if(!strcmp(obuf, "setorder")){ TCLIST *elems = tcstrsplit2(obuf, osiz); if(tclistnum(elems) > 2){ oname = tcstrdup(tclistval2(elems, 1)); otype = tcatoi(tclistval2(elems, 2)); } tclistdel(elems); } } int onsiz = oname ? strlen(oname) : 0; if(max < 1 || max > INT_MAX / 2) max = INT_MAX / 2; if(skip < 0) skip = 0; PARASEARCHARG args[num]; for(int i = 0; i < num; i++){ PARASEARCHARG *arg = args + i; arg->qry = qrys[i]; arg->res = NULL; arg->max = max; arg->skip = skip; if(pthread_create(&arg->tid, NULL, (void *(*)(void *))tcrdbparasearchworker, arg) != 0) arg->qry = NULL; } int all = 0; for(int i = 0; i < num; i++){ PARASEARCHARG *arg = args + i; if(arg->qry) pthread_join(arg->tid, NULL); if(arg->res){ tcrdbqrypopmeta(arg->qry, arg->res); all += tclistnum(arg->res); } } RDBSORTREC *recs = tcmalloc(sizeof(*recs) * all + 1); int rnum = 0; for(int i = 0; i < num; i++){ PARASEARCHARG *arg = args + i; if(arg->res){ int tnum = tclistnum(arg->res); for(int j = 0; j < tnum; j++){ int csiz; const char *cbuf = tclistval(arg->res, j, &csiz); recs[rnum].cbuf = cbuf; recs[rnum].csiz = csiz; recs[rnum].obuf = NULL; recs[rnum].osiz = 0; if(oname){ TCMAP *cols = tcstrsplit4(cbuf, csiz); int osiz; const char *obuf = tcmapget(cols, oname, onsiz, &osiz); if(obuf){ recs[rnum].obuf = tcmemdup(obuf, osiz); recs[rnum].osiz = osiz; } tcmapdel(cols); } rnum++; } } } if(oname){ int (*compar)(const RDBSORTREC *a, const RDBSORTREC *b) = NULL; switch(otype){ case RDBQOSTRASC: compar = rdbcmpsortrecstrasc; break; case RDBQOSTRDESC: compar = rdbcmpsortrecstrdesc; break; case RDBQONUMASC: compar = rdbcmpsortrecnumasc; break; case RDBQONUMDESC: compar = rdbcmpsortrecnumdesc; break; } if(compar) qsort(recs, rnum, sizeof(*recs), (int (*)(const void *, const void *))compar); for(int i = 0; i < rnum; i++){ tcfree(recs[i].obuf); } } TCLIST *res = tclistnew2(tclmin(rnum, max)); TCMAP *uset = tcmapnew2(rnum + 1); for(int i = 0; max > 0 && i < rnum; i++){ RDBSORTREC *rec = recs + i; if(tcmapputkeep(uset, rec->cbuf, rec->csiz, "", 0)){ if(skip > 0){ skip--; } else { tclistpush(res, rec->cbuf, rec->csiz); max--; } } } tcmapdel(uset); for(int i = 0; i < num; i++){ PARASEARCHARG *arg = args + i; if(arg->res) tclistdel(arg->res); } tcfree(recs); tcfree(oname); pthread_setcancelstate(ocs, NULL); return res; } /************************************************************************************************* * features for experts *************************************************************************************************/ /* Set the error code of a remote database object. */ void tcrdbsetecode(TCRDB *rdb, int ecode){ assert(rdb); pthread_setspecific(rdb->eckey, (void *)(intptr_t)ecode); } /************************************************************************************************* * private features *************************************************************************************************/ /* Lock a method of the remote database object. `rdb' specifies the remote database object. If successful, the return value is true, else, it is false. */ static bool tcrdblockmethod(TCRDB *rdb){ assert(rdb); if(pthread_mutex_lock(&rdb->mmtx) != 0){ tcrdbsetecode(rdb, TCEMISC); return false; } return true; } /* Unlock a method of the remote database object. `rdb' specifies the remote database object. */ static void tcrdbunlockmethod(TCRDB *rdb){ assert(rdb); if(pthread_mutex_unlock(&rdb->mmtx) != 0) tcrdbsetecode(rdb, TCEMISC); } /* Reconnect a remote database. `rdb' specifies the remote database object. If successful, the return value is true, else, it is false. */ static bool tcrdbreconnect(TCRDB *rdb){ assert(rdb); if(rdb->sock){ ttsockdel(rdb->sock); ttclosesock(rdb->fd); rdb->fd = -1; rdb->sock = NULL; } int fd; if(rdb->port < 1){ fd = ttopensockunix(rdb->host); } else { char addr[TTADDRBUFSIZ]; if(!ttgethostaddr(rdb->host, addr)){ tcrdbsetecode(rdb, TTENOHOST); return false; } fd = ttopensock(addr, rdb->port); } if(fd == -1){ tcrdbsetecode(rdb, TTEREFUSED); return false; } rdb->fd = fd; rdb->sock = ttsocknew(fd); return true; } /* Send data of a remote database object. `rdb' specifies the remote database object. `buf' specifies the pointer to the region of the data to send. `size' specifies the size of the buffer. If successful, the return value is true, else, it is false. */ static bool tcrdbsend(TCRDB *rdb, const void *buf, int size){ assert(rdb && buf && size >= 0); if(ttsockcheckend(rdb->sock)){ if(!(rdb->opts & RDBTRECON)) return false; tcsleep(RDBRECONWAIT); if(!tcrdbreconnect(rdb)) return false; if(ttsocksend(rdb->sock, buf, size)) return true; tcrdbsetecode(rdb, TTESEND); return false; } ttsocksetlife(rdb->sock, rdb->timeout); if(ttsocksend(rdb->sock, buf, size)) return true; tcrdbsetecode(rdb, TTESEND); if(!(rdb->opts & RDBTRECON)) return false; tcsleep(RDBRECONWAIT); if(!tcrdbreconnect(rdb)) return false; ttsocksetlife(rdb->sock, rdb->timeout); if(ttsocksend(rdb->sock, buf, size)) return true; tcrdbsetecode(rdb, TTESEND); return false; } /* Set the tuning parameters of a remote database object. `rdb' specifies the remote database object. `timeout' specifies the timeout of each query in seconds. `opts' specifies options by bitwise-or. If successful, the return value is true, else, it is false. */ static bool tcrdbtuneimpl(TCRDB *rdb, double timeout, int opts){ assert(rdb); if(rdb->fd >= 0){ tcrdbsetecode(rdb, TTEINVALID); return false; } rdb->timeout = (timeout > 0.0) ? timeout : UINT_MAX; rdb->opts = opts; return true; } /* Open a remote database. `rdb' specifies the remote database object. `host' specifies the name or the address of the server. `port' specifies the port number. If successful, the return value is true, else, it is false. */ static bool tcrdbopenimpl(TCRDB *rdb, const char *host, int port){ assert(rdb && host); if(rdb->fd >= 0){ tcrdbsetecode(rdb, TTEINVALID); return false; } int fd; if(port < 1){ fd = ttopensockunix(host); } else { char addr[TTADDRBUFSIZ]; if(!ttgethostaddr(host, addr)){ tcrdbsetecode(rdb, TTENOHOST); return false; } fd = ttopensock(addr, port); } if(fd == -1){ tcrdbsetecode(rdb, TTEREFUSED); return false; } if(rdb->host) tcfree(rdb->host); rdb->host = tcstrdup(host); rdb->port = port; rdb->expr = tcsprintf("%s:%d", host, port); rdb->fd = fd; rdb->sock = ttsocknew(fd); return true; } /* Close a remote database object. `rdb' specifies the remote database object. If successful, the return value is true, else, it is false. */ static bool tcrdbcloseimpl(TCRDB *rdb){ assert(rdb); if(rdb->fd < 0){ tcrdbsetecode(rdb, TTEINVALID); return false; } bool err = false; ttsockdel(rdb->sock); if(!ttclosesock(rdb->fd)){ tcrdbsetecode(rdb, TTEMISC); err = true; } tcfree(rdb->expr); tcfree(rdb->host); rdb->expr = NULL; rdb->host = NULL; rdb->port = -1; rdb->fd = -1; rdb->sock = NULL; return !err; } /* Store a record into a remote database object. `rdb' specifies the remote database object. `kbuf' specifies the pointer to the region of the key. `ksiz' specifies the size of the region of the key. `vbuf' specifies the pointer to the region of the value. `vsiz' specifies the size of the region of the value. If successful, the return value is true, else, it is false. */ static bool tcrdbputimpl(TCRDB *rdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){ assert(rdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0); if(rdb->fd < 0){ if(!rdb->host || !(rdb->opts & RDBTRECON)){ tcrdbsetecode(rdb, TTEINVALID); return false; } if(!tcrdbreconnect(rdb)) return false; } bool err = false; int rsiz = 2 + sizeof(uint32_t) * 2 + ksiz + vsiz; unsigned char stack[TTIOBUFSIZ]; unsigned char *buf = (rsiz < TTIOBUFSIZ) ? stack : tcmalloc(rsiz); pthread_cleanup_push(free, (buf == stack) ? NULL : buf); unsigned char *wp = buf; *(wp++) = TTMAGICNUM; *(wp++) = TTCMDPUT; uint32_t num; num = TTHTONL((uint32_t)ksiz); memcpy(wp, &num, sizeof(uint32_t)); wp += sizeof(uint32_t); num = TTHTONL((uint32_t)vsiz); memcpy(wp, &num, sizeof(uint32_t)); wp += sizeof(uint32_t); memcpy(wp, kbuf, ksiz); wp += ksiz; memcpy(wp, vbuf, vsiz); wp += vsiz; if(tcrdbsend(rdb, buf, wp - buf)){ int code = ttsockgetc(rdb->sock); if(code != 0){ tcrdbsetecode(rdb, code == -1 ? TTERECV : TTEMISC); err = true; } } else { err = true; } pthread_cleanup_pop(1); return !err; } /* Store a new record into a remote database object. `rdb' specifies the remote database object. `kbuf' specifies the pointer to the region of the key. `ksiz' specifies the size of the region of the key. `vbuf' specifies the pointer to the region of the value. `vsiz' specifies the size of the region of the value. If successful, the return value is true, else, it is false. */ static bool tcrdbputkeepimpl(TCRDB *rdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){ assert(rdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0); if(rdb->fd < 0){ if(!rdb->host || !(rdb->opts & RDBTRECON)){ tcrdbsetecode(rdb, TTEINVALID); return false; } if(!tcrdbreconnect(rdb)) return false; } bool err = false; int rsiz = 2 + sizeof(uint32_t) * 2 + ksiz + vsiz; unsigned char stack[TTIOBUFSIZ]; unsigned char *buf = (rsiz < TTIOBUFSIZ) ? stack : tcmalloc(rsiz); pthread_cleanup_push(free, (buf == stack) ? NULL : buf); unsigned char *wp = buf; *(wp++) = TTMAGICNUM; *(wp++) = TTCMDPUTKEEP; uint32_t num; num = TTHTONL((uint32_t)ksiz); memcpy(wp, &num, sizeof(uint32_t)); wp += sizeof(uint32_t); num = TTHTONL((uint32_t)vsiz); memcpy(wp, &num, sizeof(uint32_t)); wp += sizeof(uint32_t); memcpy(wp, kbuf, ksiz); wp += ksiz; memcpy(wp, vbuf, vsiz); wp += vsiz; if(tcrdbsend(rdb, buf, wp - buf)){ int code = ttsockgetc(rdb->sock); if(code != 0){ tcrdbsetecode(rdb, code == -1 ? TTERECV : TTEKEEP); err = true; } } else { err = true; } pthread_cleanup_pop(1); return !err; } /* Concatenate a value at the end of the existing record in a remote database object. `rdb' specifies the remote database object. `kbuf' specifies the pointer to the region of the key. `ksiz' specifies the size of the region of the key. `vbuf' specifies the pointer to the region of the value. `vsiz' specifies the size of the region of the value. If successful, the return value is true, else, it is false. */ static bool tcrdbputcatimpl(TCRDB *rdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){ assert(rdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0); if(rdb->fd < 0){ if(!rdb->host || !(rdb->opts & RDBTRECON)){ tcrdbsetecode(rdb, TTEINVALID); return false; } if(!tcrdbreconnect(rdb)) return false; } bool err = false; int rsiz = 2 + sizeof(uint32_t) * 2 + ksiz + vsiz; unsigned char stack[TTIOBUFSIZ]; unsigned char *buf = (rsiz < TTIOBUFSIZ) ? stack : tcmalloc(rsiz); pthread_cleanup_push(free, (buf == stack) ? NULL : buf); unsigned char *wp = buf; *(wp++) = TTMAGICNUM; *(wp++) = TTCMDPUTCAT; uint32_t num; num = TTHTONL((uint32_t)ksiz); memcpy(wp, &num, sizeof(uint32_t)); wp += sizeof(uint32_t); num = TTHTONL((uint32_t)vsiz); memcpy(wp, &num, sizeof(uint32_t)); wp += sizeof(uint32_t); memcpy(wp, kbuf, ksiz); wp += ksiz; memcpy(wp, vbuf, vsiz); wp += vsiz; if(tcrdbsend(rdb, buf, wp - buf)){ int code = ttsockgetc(rdb->sock); if(code != 0){ tcrdbsetecode(rdb, code == -1 ? TTERECV : TTEMISC); err = true; } } else { err = true; } pthread_cleanup_pop(1); return !err; } /* Concatenate a value at the end of the existing record and shift it to the left. `rdb' specifies the remote database object. `kbuf' specifies the pointer to the region of the key. `ksiz' specifies the size of the region of the key. `vbuf' specifies the pointer to the region of the value. `vsiz' specifies the size of the region of the value. `width' specifies the width of the record. If successful, the return value is true, else, it is false. */ static bool tcrdbputshlimpl(TCRDB *rdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz, int width){ assert(rdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0 && width >= 0); if(rdb->fd < 0){ if(!rdb->host || !(rdb->opts & RDBTRECON)){ tcrdbsetecode(rdb, TTEINVALID); return false; } if(!tcrdbreconnect(rdb)) return false; } bool err = false; int rsiz = 2 + sizeof(uint32_t) * 3 + ksiz + vsiz; unsigned char stack[TTIOBUFSIZ]; unsigned char *buf = (rsiz < TTIOBUFSIZ) ? stack : tcmalloc(rsiz); pthread_cleanup_push(free, (buf == stack) ? NULL : buf); unsigned char *wp = buf; *(wp++) = TTMAGICNUM; *(wp++) = TTCMDPUTSHL; uint32_t num; num = TTHTONL((uint32_t)ksiz); memcpy(wp, &num, sizeof(uint32_t)); wp += sizeof(uint32_t); num = TTHTONL((uint32_t)vsiz); memcpy(wp, &num, sizeof(uint32_t)); wp += sizeof(uint32_t); num = TTHTONL((uint32_t)width); memcpy(wp, &num, sizeof(uint32_t)); wp += sizeof(uint32_t); memcpy(wp, kbuf, ksiz); wp += ksiz; memcpy(wp, vbuf, vsiz); wp += vsiz; if(tcrdbsend(rdb, buf, wp - buf)){ int code = ttsockgetc(rdb->sock); if(code != 0){ tcrdbsetecode(rdb, code == -1 ? TTERECV : TTEMISC); err = true; } } else { err = true; } pthread_cleanup_pop(1); return !err; } /* Store a record into a remote database object without response from the server. `rdb' specifies the remote database object. `kbuf' specifies the pointer to the region of the key. `ksiz' specifies the size of the region of the key. `vbuf' specifies the pointer to the region of the value. `vsiz' specifies the size of the region of the value. If successful, the return value is true, else, it is false. */ static bool tcrdbputnrimpl(TCRDB *rdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){ assert(rdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0); if(rdb->fd < 0){ if(!rdb->host || !(rdb->opts & RDBTRECON)){ tcrdbsetecode(rdb, TTEINVALID); return false; } if(!tcrdbreconnect(rdb)) return false; } bool err = false; int rsiz = 2 + sizeof(uint32_t) * 2 + ksiz + vsiz; unsigned char stack[TTIOBUFSIZ]; unsigned char *buf = (rsiz < TTIOBUFSIZ) ? stack : tcmalloc(rsiz); pthread_cleanup_push(free, (buf == stack) ? NULL : buf); unsigned char *wp = buf; *(wp++) = TTMAGICNUM; *(wp++) = TTCMDPUTNR; uint32_t num; num = TTHTONL((uint32_t)ksiz); memcpy(wp, &num, sizeof(uint32_t)); wp += sizeof(uint32_t); num = TTHTONL((uint32_t)vsiz); memcpy(wp, &num, sizeof(uint32_t)); wp += sizeof(uint32_t); memcpy(wp, kbuf, ksiz); wp += ksiz; memcpy(wp, vbuf, vsiz); wp += vsiz; if(!tcrdbsend(rdb, buf, wp - buf)) err = true; pthread_cleanup_pop(1); return !err; } /* Remove a record of a remote database object. `rdb' specifies the remote database object. `kbuf' specifies the pointer to the region of the key. `ksiz' specifies the size of the region of the key. If successful, the return value is true, else, it is false. */ static bool tcrdboutimpl(TCRDB *rdb, const void *kbuf, int ksiz){ assert(rdb && kbuf && ksiz >= 0); if(rdb->fd < 0){ if(!rdb->host || !(rdb->opts & RDBTRECON)){ tcrdbsetecode(rdb, TTEINVALID); return false; } if(!tcrdbreconnect(rdb)) return false; } bool err = false; int rsiz = 2 + sizeof(uint32_t) + ksiz; unsigned char stack[TTIOBUFSIZ]; unsigned char *buf = (rsiz < TTIOBUFSIZ) ? stack : tcmalloc(rsiz); pthread_cleanup_push(free, (buf == stack) ? NULL : buf); unsigned char *wp = buf; *(wp++) = TTMAGICNUM; *(wp++) = TTCMDOUT; uint32_t num; num = TTHTONL((uint32_t)ksiz); memcpy(wp, &num, sizeof(uint32_t)); wp += sizeof(uint32_t); memcpy(wp, kbuf, ksiz); wp += ksiz; if(tcrdbsend(rdb, buf, wp - buf)){ int code = ttsockgetc(rdb->sock); if(code != 0){ tcrdbsetecode(rdb, code == -1 ? TTERECV : TTENOREC); err = true; } } else { err = true; } pthread_cleanup_pop(1); return !err; } /* Retrieve a record in a remote database object. `rdb' specifies the remote database object. `kbuf' specifies the pointer to the region of the key. `ksiz' specifies the size of the region of the key. `sp' specifies the pointer to the variable into which the size of the region of the return value is assigned. If successful, the return value is the pointer to the region of the value of the corresponding record. */ static void *tcrdbgetimpl(TCRDB *rdb, const void *kbuf, int ksiz, int *sp){ assert(rdb && kbuf && ksiz >= 0 && sp); if(rdb->fd < 0){ if(!rdb->host || !(rdb->opts & RDBTRECON)){ tcrdbsetecode(rdb, TTEINVALID); return NULL; } if(!tcrdbreconnect(rdb)) return NULL; } char *vbuf = NULL; int rsiz = 2 + sizeof(uint32_t) + ksiz; unsigned char stack[TTIOBUFSIZ]; unsigned char *buf = (rsiz < TTIOBUFSIZ) ? stack : tcmalloc(rsiz); pthread_cleanup_push(free, (buf == stack) ? NULL : buf); unsigned char *wp = buf; *(wp++) = TTMAGICNUM; *(wp++) = TTCMDGET; uint32_t num; num = TTHTONL((uint32_t)ksiz); memcpy(wp, &num, sizeof(uint32_t)); wp += sizeof(uint32_t); memcpy(wp, kbuf, ksiz); wp += ksiz; if(tcrdbsend(rdb, buf, wp - buf)){ int code = ttsockgetc(rdb->sock); if(code == 0){ int vsiz = ttsockgetint32(rdb->sock); if(!ttsockcheckend(rdb->sock) && vsiz >= 0){ vbuf = tcmalloc(vsiz + 1); if(ttsockrecv(rdb->sock, vbuf, vsiz)){ vbuf[vsiz] = '\0'; *sp = vsiz; } else { tcrdbsetecode(rdb, TTERECV); tcfree(vbuf); vbuf = NULL; } } else { tcrdbsetecode(rdb, TTERECV); } } else { tcrdbsetecode(rdb, code == -1 ? TTERECV : TTENOREC); } } pthread_cleanup_pop(1); return vbuf; } /* Retrieve records in a remote database object. `rdb' specifies the remote database object. `recs' specifies a map object containing the retrieval keys. If successful, the return value is true, else, it is false. */ static bool tcrdbmgetimpl(TCRDB *rdb, TCMAP *recs){ assert(rdb && recs); if(rdb->fd < 0){ if(!rdb->host || !(rdb->opts & RDBTRECON)){ tcrdbsetecode(rdb, TTEINVALID); return false; } if(!tcrdbreconnect(rdb)) return false; } bool err = false; TCXSTR *xstr = tcxstrnew(); pthread_cleanup_push((void (*)(void *))tcxstrdel, xstr); uint8_t magic[2]; magic[0] = TTMAGICNUM; magic[1] = TTCMDMGET; tcxstrcat(xstr, magic, sizeof(magic)); uint32_t num; num = (uint32_t)tcmaprnum(recs); num = TTHTONL(num); tcxstrcat(xstr, &num, sizeof(num)); tcmapiterinit(recs); const char *kbuf; int ksiz; while((kbuf = tcmapiternext(recs, &ksiz)) != NULL){ num = TTHTONL((uint32_t)ksiz); tcxstrcat(xstr, &num, sizeof(num)); tcxstrcat(xstr, kbuf, ksiz); } tcmapclear(recs); char stack[TTIOBUFSIZ]; if(tcrdbsend(rdb, tcxstrptr(xstr), tcxstrsize(xstr))){ int code = ttsockgetc(rdb->sock); int rnum = ttsockgetint32(rdb->sock); if(code == 0){ if(!ttsockcheckend(rdb->sock) && rnum >= 0){ for(int i = 0; i < rnum; i++){ int rksiz = ttsockgetint32(rdb->sock); int rvsiz = ttsockgetint32(rdb->sock); if(ttsockcheckend(rdb->sock)){ tcrdbsetecode(rdb, TTERECV); err = true; break; } int rsiz = rksiz + rvsiz; char *rbuf = (rsiz < TTIOBUFSIZ) ? stack : tcmalloc(rsiz + 1); if(ttsockrecv(rdb->sock, rbuf, rsiz)){ tcmapput(recs, rbuf, rksiz, rbuf + rksiz, rvsiz); } else { tcrdbsetecode(rdb, TTERECV); err = true; } if(rbuf != stack) tcfree(rbuf); } } else { tcrdbsetecode(rdb, TTERECV); err = true; } } else { tcrdbsetecode(rdb, code == -1 ? TTERECV : TTENOREC); err = true; } } else { err = true; } pthread_cleanup_pop(1); return !err; } /* Get the size of the value of a record in a remote database object. `rdb' specifies the remote database object. `kbuf' specifies the pointer to the region of the key. `ksiz' specifies the size of the region of the key. If successful, the return value is the size of the value of the corresponding record, else, it is -1. */ static int tcrdbvsizimpl(TCRDB *rdb, const void *kbuf, int ksiz){ assert(rdb && kbuf && ksiz >= 0); if(rdb->fd < 0){ if(!rdb->host || !(rdb->opts & RDBTRECON)){ tcrdbsetecode(rdb, TTEINVALID); return -1; } if(!tcrdbreconnect(rdb)) return -1; } int vsiz = -1; int rsiz = 2 + sizeof(uint32_t) + ksiz; unsigned char stack[TTIOBUFSIZ]; unsigned char *buf = (rsiz < TTIOBUFSIZ) ? stack : tcmalloc(rsiz); pthread_cleanup_push(free, (buf == stack) ? NULL : buf); unsigned char *wp = buf; *(wp++) = TTMAGICNUM; *(wp++) = TTCMDVSIZ; uint32_t num; num = TTHTONL((uint32_t)ksiz); memcpy(wp, &num, sizeof(uint32_t)); wp += sizeof(uint32_t); memcpy(wp, kbuf, ksiz); wp += ksiz; if(tcrdbsend(rdb, buf, wp - buf)){ int code = ttsockgetc(rdb->sock); if(code == 0){ vsiz = ttsockgetint32(rdb->sock); if(ttsockcheckend(rdb->sock)){ tcrdbsetecode(rdb, TTERECV); vsiz = -1; } } else { tcrdbsetecode(rdb, code == -1 ? TTERECV : TTENOREC); } } pthread_cleanup_pop(1); return vsiz; } /* Initialize the iterator of a remote database object. `rdb' specifies the remote database object. If successful, the return value is true, else, it is false. */ static bool tcrdbiterinitimpl(TCRDB *rdb){ assert(rdb); if(rdb->fd < 0){ if(!rdb->host || !(rdb->opts & RDBTRECON)){ tcrdbsetecode(rdb, TTEINVALID); return false; } if(!tcrdbreconnect(rdb)) return false; } bool err = false; unsigned char buf[TTIOBUFSIZ]; unsigned char *wp = buf; *(wp++) = TTMAGICNUM; *(wp++) = TTCMDITERINIT; if(tcrdbsend(rdb, buf, wp - buf)){ int code = ttsockgetc(rdb->sock); if(code != 0){ tcrdbsetecode(rdb, code == -1 ? TTERECV : TTEMISC); err = true; } } else { err = true; } return !err; } /* Get the next key of the iterator of a remote database object. `rdb' specifies the remote database object. `sp' specifies the pointer to the variable into which the size of the region of the return value is assigned. If successful, the return value is the pointer to the region of the next key, else, it is `NULL'. */ static void *tcrdbiternextimpl(TCRDB *rdb, int *sp){ assert(rdb && sp); if(rdb->fd < 0){ if(!rdb->host || !(rdb->opts & RDBTRECON)){ tcrdbsetecode(rdb, TTEINVALID); return NULL; } if(!tcrdbreconnect(rdb)) return NULL; } char *vbuf = NULL; unsigned char buf[TTIOBUFSIZ]; unsigned char *wp = buf; *(wp++) = TTMAGICNUM; *(wp++) = TTCMDITERNEXT; if(tcrdbsend(rdb, buf, wp - buf)){ int code = ttsockgetc(rdb->sock); if(code == 0){ int vsiz = ttsockgetint32(rdb->sock); if(!ttsockcheckend(rdb->sock) && vsiz >= 0){ vbuf = tcmalloc(vsiz + 1); if(ttsockrecv(rdb->sock, vbuf, vsiz)){ vbuf[vsiz] = '\0'; *sp = vsiz; } else { tcrdbsetecode(rdb, TTERECV); tcfree(vbuf); vbuf = NULL; } } else { tcrdbsetecode(rdb, TTERECV); } } else { tcrdbsetecode(rdb, code == -1 ? TTERECV : TTENOREC); } } return vbuf; } /* Get forward matching keys in a remote database object. `rdb' specifies the remote database object. `pbuf' specifies the pointer to the region of the prefix. `psiz' specifies the size of the region of the prefix. `max' specifies the maximum number of keys to be fetched. The return value is a list object of the corresponding keys. */ static TCLIST *tcrdbfwmkeysimpl(TCRDB *rdb, const void *pbuf, int psiz, int max){ assert(rdb && pbuf && psiz >= 0); TCLIST *keys = tclistnew(); if(rdb->fd < 0){ if(!rdb->host || !(rdb->opts & RDBTRECON)){ tcrdbsetecode(rdb, TTEINVALID); return NULL; } if(!tcrdbreconnect(rdb)) return NULL; } int rsiz = 2 + sizeof(uint32_t) * 2 + psiz; if(max < 0) max = INT_MAX; unsigned char stack[TTIOBUFSIZ]; unsigned char *buf = (rsiz < TTIOBUFSIZ) ? stack : tcmalloc(rsiz); pthread_cleanup_push(free, (buf == stack) ? NULL : buf); unsigned char *wp = buf; *(wp++) = TTMAGICNUM; *(wp++) = TTCMDFWMKEYS; uint32_t num; num = TTHTONL((uint32_t)psiz); memcpy(wp, &num, sizeof(uint32_t)); wp += sizeof(uint32_t); num = TTHTONL((uint32_t)max); memcpy(wp, &num, sizeof(uint32_t)); wp += sizeof(uint32_t); memcpy(wp, pbuf, psiz); wp += psiz; if(tcrdbsend(rdb, buf, wp - buf)){ int code = ttsockgetc(rdb->sock); if(code == 0){ int knum = ttsockgetint32(rdb->sock); if(!ttsockcheckend(rdb->sock) && knum >= 0){ for(int i = 0; i < knum; i++){ int ksiz = ttsockgetint32(rdb->sock); if(ttsockcheckend(rdb->sock)){ tcrdbsetecode(rdb, TTERECV); break; } char *kbuf = (ksiz < TTIOBUFSIZ) ? stack : tcmalloc(ksiz + 1); if(ttsockrecv(rdb->sock, kbuf, ksiz)){ tclistpush(keys, kbuf, ksiz); } else { tcrdbsetecode(rdb, TTERECV); } if(kbuf != (char *)stack) tcfree(kbuf); } } else { tcrdbsetecode(rdb, TTERECV); } } else { tcrdbsetecode(rdb, code == -1 ? TTERECV : TTENOREC); } } pthread_cleanup_pop(1); return keys; } /* Add an integer to a record in a remote database object. `rdb' specifies the remote database object connected as a writer. `kbuf' specifies the pointer to the region of the key. `ksiz' specifies the size of the region of the key. `num' specifies the additional value. If successful, the return value is the summation value, else, it is `INT_MIN'. */ static int tcrdbaddintimpl(TCRDB *rdb, const void *kbuf, int ksiz, int num){ assert(rdb && kbuf && ksiz >= 0); if(rdb->fd < 0){ if(!rdb->host || !(rdb->opts & RDBTRECON)){ tcrdbsetecode(rdb, TTEINVALID); return INT_MIN; } if(!tcrdbreconnect(rdb)) return INT_MIN; } int sum = INT_MIN; int rsiz = 2 + sizeof(uint32_t) * 2 + ksiz; unsigned char stack[TTIOBUFSIZ]; unsigned char *buf = (rsiz < TTIOBUFSIZ) ? stack : tcmalloc(rsiz); pthread_cleanup_push(free, (buf == stack) ? NULL : buf); unsigned char *wp = buf; *(wp++) = TTMAGICNUM; *(wp++) = TTCMDADDINT; uint32_t lnum; lnum = TTHTONL((uint32_t)ksiz); memcpy(wp, &lnum, sizeof(uint32_t)); wp += sizeof(uint32_t); lnum = TTHTONL((uint32_t)num); memcpy(wp, &lnum, sizeof(uint32_t)); wp += sizeof(uint32_t); memcpy(wp, kbuf, ksiz); wp += ksiz; if(tcrdbsend(rdb, buf, wp - buf)){ int code = ttsockgetc(rdb->sock); if(code == 0){ sum = ttsockgetint32(rdb->sock); if(ttsockcheckend(rdb->sock)){ tcrdbsetecode(rdb, TTERECV); sum = -1; } } else { tcrdbsetecode(rdb, code == -1 ? TTERECV : TTEKEEP); } } pthread_cleanup_pop(1); return sum; } /* Add a real number to a record in a remote database object. `rdb' specifies the remote database object connected as a writer. `kbuf' specifies the pointer to the region of the key. `ksiz' specifies the size of the region of the key. `num' specifies the additional value. If successful, the return value is the summation value, else, it is Not-a-Number. */ static double tcrdbadddoubleimpl(TCRDB *rdb, const void *kbuf, int ksiz, double num){ assert(rdb && kbuf && ksiz >= 0); if(rdb->fd < 0){ if(!rdb->host || !(rdb->opts & RDBTRECON)){ tcrdbsetecode(rdb, TTEINVALID); return nan(""); } if(!tcrdbreconnect(rdb)) return nan(""); } double sum = nan(""); int rsiz = 2 + sizeof(uint32_t) + sizeof(uint64_t) * 2 + ksiz; unsigned char stack[TTIOBUFSIZ]; unsigned char *buf = (rsiz < TTIOBUFSIZ) ? stack : tcmalloc(rsiz); pthread_cleanup_push(free, (buf == stack) ? NULL : buf); unsigned char *wp = buf; *(wp++) = TTMAGICNUM; *(wp++) = TTCMDADDDOUBLE; uint32_t lnum; lnum = TTHTONL((uint32_t)ksiz); memcpy(wp, &lnum, sizeof(uint32_t)); wp += sizeof(uint32_t); char dbuf[sizeof(uint64_t)*2]; ttpackdouble(num, (char *)wp); wp += sizeof(dbuf); memcpy(wp, kbuf, ksiz); wp += ksiz; if(tcrdbsend(rdb, buf, wp - buf)){ int code = ttsockgetc(rdb->sock); if(code == 0){ if(ttsockrecv(rdb->sock, dbuf, sizeof(dbuf)) && !ttsockcheckend(rdb->sock)){ sum = ttunpackdouble(dbuf); } else { tcrdbsetecode(rdb, TTERECV); } } else { tcrdbsetecode(rdb, code == -1 ? TTERECV : TTEKEEP); } } pthread_cleanup_pop(1); return sum; } /* Call a function of the scripting language extension. `rdb' specifies the remote database object. `name' specifies the function name. `opts' specifies options by bitwise-or. `kbuf' specifies the pointer to the region of the key. `ksiz' specifies the size of the region of the key. `vbuf' specifies the pointer to the region of the value. `vsiz' specifies the size of the region of the value. `sp' specifies the pointer to the variable into which the size of the region of the return value is assigned. If successful, the return value is the pointer to the region of the value of the response. */ static void *tcrdbextimpl(TCRDB *rdb, const char *name, int opts, const void *kbuf, int ksiz, const void *vbuf, int vsiz, int *sp){ assert(rdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0); if(rdb->fd < 0){ if(!rdb->host || !(rdb->opts & RDBTRECON)){ tcrdbsetecode(rdb, TTEINVALID); return NULL; } if(!tcrdbreconnect(rdb)) return NULL; } char *xbuf = NULL; int nsiz = strlen(name); int rsiz = 2 + sizeof(uint32_t) * 4 + nsiz + ksiz + vsiz; unsigned char stack[TTIOBUFSIZ]; unsigned char *buf = (rsiz < TTIOBUFSIZ) ? stack : tcmalloc(rsiz); pthread_cleanup_push(free, (buf == stack) ? NULL : buf); unsigned char *wp = buf; *(wp++) = TTMAGICNUM; *(wp++) = TTCMDEXT; uint32_t num; num = TTHTONL((uint32_t)nsiz); memcpy(wp, &num, sizeof(uint32_t)); wp += sizeof(uint32_t); num = TTHTONL((uint32_t)opts); memcpy(wp, &num, sizeof(uint32_t)); wp += sizeof(uint32_t); num = TTHTONL((uint32_t)ksiz); memcpy(wp, &num, sizeof(uint32_t)); wp += sizeof(uint32_t); num = TTHTONL((uint32_t)vsiz); memcpy(wp, &num, sizeof(uint32_t)); wp += sizeof(uint32_t); memcpy(wp, name, nsiz); wp += nsiz; memcpy(wp, kbuf, ksiz); wp += ksiz; memcpy(wp, vbuf, vsiz); wp += vsiz; if(tcrdbsend(rdb, buf, wp - buf)){ int code = ttsockgetc(rdb->sock); if(code == 0){ int xsiz = ttsockgetint32(rdb->sock); if(!ttsockcheckend(rdb->sock) && xsiz >= 0){ xbuf = tcmalloc(xsiz + 1); if(ttsockrecv(rdb->sock, xbuf, xsiz)){ xbuf[xsiz] = '\0'; *sp = xsiz; } else { tcrdbsetecode(rdb, TTERECV); tcfree(xbuf); xbuf = NULL; } } else { tcrdbsetecode(rdb, TTERECV); } } else { tcrdbsetecode(rdb, code == -1 ? TTERECV : TTEMISC); } } pthread_cleanup_pop(1); return xbuf; } /* Synchronize updated contents of a remote database object with the file and the device. `rdb' specifies the remote database object. If successful, the return value is true, else, it is false. */ static bool tcrdbsyncimpl(TCRDB *rdb){ assert(rdb); if(rdb->fd < 0){ if(!rdb->host || !(rdb->opts & RDBTRECON)){ tcrdbsetecode(rdb, TTEINVALID); return false; } if(!tcrdbreconnect(rdb)) return false; } bool err = false; unsigned char buf[TTIOBUFSIZ]; unsigned char *wp = buf; *(wp++) = TTMAGICNUM; *(wp++) = TTCMDSYNC; if(tcrdbsend(rdb, buf, wp - buf)){ int code = ttsockgetc(rdb->sock); if(code != 0){ tcrdbsetecode(rdb, code == -1 ? TTERECV : TTEMISC); err = true; } } else { err = true; } return !err; } /* Optimize the storage of a remove database object. `rdb' specifies the remote database object. `params' specifies the string of the tuning parameters. If successful, the return value is true, else, it is false. */ static bool tcrdboptimizeimpl(TCRDB *rdb, const char *params){ assert(rdb); if(rdb->fd < 0){ if(!rdb->host || !(rdb->opts & RDBTRECON)){ tcrdbsetecode(rdb, TTEINVALID); return false; } if(!tcrdbreconnect(rdb)) return false; } if(!params) params = ""; int psiz = strlen(params); bool err = false; int rsiz = 2 + sizeof(uint32_t) + psiz; unsigned char stack[TTIOBUFSIZ]; unsigned char *buf = (rsiz < TTIOBUFSIZ) ? stack : tcmalloc(rsiz); pthread_cleanup_push(free, (buf == stack) ? NULL : buf); unsigned char *wp = buf; *(wp++) = TTMAGICNUM; *(wp++) = TTCMDOPTIMIZE; uint32_t num; num = TTHTONL((uint32_t)psiz); memcpy(wp, &num, sizeof(uint32_t)); wp += sizeof(uint32_t); memcpy(wp, params, psiz); wp += psiz; if(tcrdbsend(rdb, buf, wp - buf)){ int code = ttsockgetc(rdb->sock); if(code != 0){ tcrdbsetecode(rdb, code == -1 ? TTERECV : TTEMISC); err = true; } } else { err = true; } pthread_cleanup_pop(1); return !err; } /* Remove all records of a remote database object. `rdb' specifies the remote database object. If successful, the return value is true, else, it is false. */ static bool tcrdbvanishimpl(TCRDB *rdb){ assert(rdb); if(rdb->fd < 0){ if(!rdb->host || !(rdb->opts & RDBTRECON)){ tcrdbsetecode(rdb, TTEINVALID); return false; } if(!tcrdbreconnect(rdb)) return false; } bool err = false; unsigned char buf[TTIOBUFSIZ]; unsigned char *wp = buf; *(wp++) = TTMAGICNUM; *(wp++) = TTCMDVANISH; if(tcrdbsend(rdb, buf, wp - buf)){ int code = ttsockgetc(rdb->sock); if(code != 0){ tcrdbsetecode(rdb, code == -1 ? TTERECV : TTEMISC); err = true; } } else { err = true; } return !err; } /* Copy the database file of a remote database object. `rdb' specifies the remote database object. `path' specifies the path of the destination file. If successful, the return value is true, else, it is false. */ static bool tcrdbcopyimpl(TCRDB *rdb, const char *path){ assert(rdb && path); if(rdb->fd < 0){ if(!rdb->host || !(rdb->opts & RDBTRECON)){ tcrdbsetecode(rdb, TTEINVALID); return false; } if(!tcrdbreconnect(rdb)) return false; } bool err = false; int psiz = strlen(path); int rsiz = 2 + sizeof(uint32_t) + psiz; unsigned char stack[TTIOBUFSIZ]; unsigned char *buf = (rsiz < TTIOBUFSIZ) ? stack : tcmalloc(rsiz); pthread_cleanup_push(free, (buf == stack) ? NULL : buf); unsigned char *wp = buf; *(wp++) = TTMAGICNUM; *(wp++) = TTCMDCOPY; uint32_t num; num = TTHTONL((uint32_t)psiz); memcpy(wp, &num, sizeof(uint32_t)); wp += sizeof(uint32_t); memcpy(wp, path, psiz); wp += psiz; if(tcrdbsend(rdb, buf, wp - buf)){ int code = ttsockgetc(rdb->sock); if(code != 0){ tcrdbsetecode(rdb, code == -1 ? TTERECV : TTEMISC); err = true; } } else { err = true; } pthread_cleanup_pop(1); return !err; } /* Restore the database file of a remote database object from the update log. `rdb' specifies the remote database object. `path' specifies the path of the update log directory. `ts' specifies the beginning timestamp in microseconds. `opts' specifies options by bitwise-or. If successful, the return value is true, else, it is false. */ static bool tcrdbrestoreimpl(TCRDB *rdb, const char *path, uint64_t ts, int opts){ assert(rdb && path); if(rdb->fd < 0){ if(!rdb->host || !(rdb->opts & RDBTRECON)){ tcrdbsetecode(rdb, TTEINVALID); return false; } if(!tcrdbreconnect(rdb)) return false; } bool err = false; int psiz = strlen(path); int rsiz = 2 + sizeof(uint32_t) + sizeof(uint64_t) + sizeof(uint32_t) + psiz; unsigned char stack[TTIOBUFSIZ]; unsigned char *buf = (rsiz < TTIOBUFSIZ) ? stack : tcmalloc(rsiz); pthread_cleanup_push(free, (buf == stack) ? NULL : buf); unsigned char *wp = buf; *(wp++) = TTMAGICNUM; *(wp++) = TTCMDRESTORE; uint32_t lnum = TTHTONL((uint32_t)psiz); memcpy(wp, &lnum, sizeof(uint32_t)); wp += sizeof(uint32_t); uint64_t llnum = TTHTONLL(ts); memcpy(wp, &llnum, sizeof(uint64_t)); wp += sizeof(uint64_t); lnum = TTHTONL((uint32_t)opts); memcpy(wp, &lnum, sizeof(uint32_t)); wp += sizeof(uint32_t); memcpy(wp, path, psiz); wp += psiz; if(tcrdbsend(rdb, buf, wp - buf)){ int code = ttsockgetc(rdb->sock); if(code != 0){ tcrdbsetecode(rdb, code == -1 ? TTERECV : TTEMISC); err = true; } } else { err = true; } pthread_cleanup_pop(1); return !err; } /* Set the replication master of a remote database object. `rdb' specifies the remote database object. `host' specifies the name or the address of the server. `port' specifies the port number. `ts' specifies the beginning timestamp in microseconds. `opts' specifies options by bitwise-or: `RDBROCHKCON' for consistency checking. If successful, the return value is true, else, it is false. */ static bool tcrdbsetmstimpl(TCRDB *rdb, const char *host, int port, uint64_t ts, int opts){ assert(rdb); if(rdb->fd < 0){ if(!rdb->host || !(rdb->opts & RDBTRECON)){ tcrdbsetecode(rdb, TTEINVALID); return false; } if(!tcrdbreconnect(rdb)) return false; } if(!host) host = ""; if(port < 0) port = 0; bool err = false; int hsiz = strlen(host); int rsiz = 2 + sizeof(uint32_t) * 3 + hsiz; unsigned char stack[TTIOBUFSIZ]; unsigned char *buf = (rsiz < TTIOBUFSIZ) ? stack : tcmalloc(rsiz); pthread_cleanup_push(free, (buf == stack) ? NULL : buf); unsigned char *wp = buf; *(wp++) = TTMAGICNUM; *(wp++) = TTCMDSETMST; uint32_t lnum; lnum = TTHTONL((uint32_t)hsiz); memcpy(wp, &lnum, sizeof(uint32_t)); wp += sizeof(uint32_t); lnum = TTHTONL((uint32_t)port); memcpy(wp, &lnum, sizeof(uint32_t)); wp += sizeof(uint32_t); uint64_t llnum; llnum = TTHTONLL(ts); memcpy(wp, &llnum, sizeof(uint64_t)); wp += sizeof(uint64_t); lnum = TTHTONL((uint32_t)opts); memcpy(wp, &lnum, sizeof(uint32_t)); wp += sizeof(uint32_t); memcpy(wp, host, hsiz); wp += hsiz; if(tcrdbsend(rdb, buf, wp - buf)){ int code = ttsockgetc(rdb->sock); if(code != 0){ tcrdbsetecode(rdb, code == -1 ? TTERECV : TTEMISC); err = true; } } else { err = true; } pthread_cleanup_pop(1); return !err; } /* Get the simple server expression of an abstract database object. `rdb' specifies the remote database object. The return value is the simple server expression or `NULL' if the object does not connect to any database server. */ const char *tcrdbexprimpl(TCRDB *rdb){ assert(rdb); if(!rdb->host){ tcrdbsetecode(rdb, TTEINVALID); return NULL; } return rdb->expr; } /* Get the number of records of a remote database object. `rdb' specifies the remote database object. The return value is the number of records or 0 if the object does not connect to any database server. */ static uint64_t tcrdbrnumimpl(TCRDB *rdb){ assert(rdb); if(rdb->fd < 0){ if(!rdb->host || !(rdb->opts & RDBTRECON)){ tcrdbsetecode(rdb, TTEINVALID); return 0; } if(!tcrdbreconnect(rdb)) return 0; } unsigned char buf[TTIOBUFSIZ]; unsigned char *wp = buf; *(wp++) = TTMAGICNUM; *(wp++) = TTCMDRNUM; uint64_t rnum = 0; if(tcrdbsend(rdb, buf, wp - buf)){ int code = ttsockgetc(rdb->sock); if(code == 0){ rnum = ttsockgetint64(rdb->sock); if(ttsockcheckend(rdb->sock)){ rnum = 0; tcrdbsetecode(rdb, TTERECV); } } else { tcrdbsetecode(rdb, code == -1 ? TTERECV : TTEMISC); } } return rnum; } /* Get the size of the database of a remote database object. `rdb' specifies the remote database object. The return value is the size of the database or 0 if the object does not connect to any database server. */ static uint64_t tcrdbsizeimpl(TCRDB *rdb){ assert(rdb); if(rdb->fd < 0){ if(!rdb->host || !(rdb->opts & RDBTRECON)){ tcrdbsetecode(rdb, TTEINVALID); return 0; } if(!tcrdbreconnect(rdb)) return 0; } unsigned char buf[TTIOBUFSIZ]; unsigned char *wp = buf; *(wp++) = TTMAGICNUM; *(wp++) = TTCMDSIZE; uint64_t size = 0; if(tcrdbsend(rdb, buf, wp - buf)){ int code = ttsockgetc(rdb->sock); if(code == 0){ size = ttsockgetint64(rdb->sock); if(ttsockcheckend(rdb->sock)){ size = 0; tcrdbsetecode(rdb, TTERECV); } } else { tcrdbsetecode(rdb, code == -1 ? TTERECV : TTEMISC); } } return size; } /* Get the status string of the database of a remote database object. `rdb' specifies the remote database object. The return value is the status message of the database or `NULL' if the object does not connect to any database server. */ static char *tcrdbstatimpl(TCRDB *rdb){ assert(rdb); if(rdb->fd < 0){ if(!rdb->host || !(rdb->opts & RDBTRECON)){ tcrdbsetecode(rdb, TTEINVALID); return NULL; } if(!tcrdbreconnect(rdb)) return NULL; } unsigned char buf[TTIOBUFSIZ]; unsigned char *wp = buf; *(wp++) = TTMAGICNUM; *(wp++) = TTCMDSTAT; uint32_t size = 0; if(tcrdbsend(rdb, buf, wp - buf)){ int code = ttsockgetc(rdb->sock); if(code == 0){ size = ttsockgetint32(rdb->sock); if(ttsockcheckend(rdb->sock) || size >= TTIOBUFSIZ || !ttsockrecv(rdb->sock, (char *)buf, size)){ size = 0; tcrdbsetecode(rdb, TTERECV); } } else { tcrdbsetecode(rdb, code == -1 ? TTERECV : TTEMISC); } } if(size < 1){ tcrdbsetecode(rdb, TTEMISC); return NULL; } return tcmemdup(buf, size); } /* Call a versatile function for miscellaneous operations of a remote database object. `rdb' specifies the remote database object. `name' specifies the name of the function. `opts' specifies options by bitwise-or. `args' specifies a list object containing arguments. If successful, the return value is a list object of the result. */ static TCLIST *tcrdbmiscimpl(TCRDB *rdb, const char *name, int opts, const TCLIST *args){ assert(rdb && name && args); if(rdb->fd < 0){ if(!rdb->host || !(rdb->opts & RDBTRECON)){ tcrdbsetecode(rdb, TTEINVALID); return NULL; } if(!tcrdbreconnect(rdb)) return NULL; } bool err = false; TCLIST *res = NULL; TCXSTR *xstr = tcxstrnew(); pthread_cleanup_push((void (*)(void *))tcxstrdel, xstr); uint8_t magic[2]; magic[0] = TTMAGICNUM; magic[1] = TTCMDMISC; tcxstrcat(xstr, magic, sizeof(magic)); int nsiz = strlen(name); uint32_t num; num = TTHTONL((uint32_t)nsiz); tcxstrcat(xstr, &num, sizeof(num)); num = TTHTONL((uint32_t)opts); tcxstrcat(xstr, &num, sizeof(num)); num = tclistnum(args); num = TTHTONL(num); tcxstrcat(xstr, &num, sizeof(num)); tcxstrcat(xstr, name, nsiz); for(int i = 0; i < tclistnum(args); i++){ int rsiz; const char *rbuf = tclistval(args, i, &rsiz); num = TTHTONL((uint32_t)rsiz); tcxstrcat(xstr, &num, sizeof(num)); tcxstrcat(xstr, rbuf, rsiz); } char stack[TTIOBUFSIZ]; if(tcrdbsend(rdb, tcxstrptr(xstr), tcxstrsize(xstr))){ int code = ttsockgetc(rdb->sock); int rnum = ttsockgetint32(rdb->sock); if(code == 0){ if(!ttsockcheckend(rdb->sock) && rnum >= 0){ res = tclistnew2(rnum); for(int i = 0; i < rnum; i++){ int esiz = ttsockgetint32(rdb->sock); if(ttsockcheckend(rdb->sock)){ tcrdbsetecode(rdb, TTERECV); err = true; break; } char *ebuf = (esiz < TTIOBUFSIZ) ? stack : tcmalloc(esiz + 1); if(ttsockrecv(rdb->sock, ebuf, esiz)){ tclistpush(res, ebuf, esiz); } else { tcrdbsetecode(rdb, TTERECV); err = true; } if(ebuf != stack) tcfree(ebuf); } } else { tcrdbsetecode(rdb, TTERECV); err = true; } } else { tcrdbsetecode(rdb, code == -1 ? TTERECV : TTEMISC); err = true; } } else { err = true; } pthread_cleanup_pop(1); if(res && err){ tclistdel(res); res = NULL; } return res; } /* Pop meta data from the result list to the member of the query object. `qry' specifies the query object. `res' specifies the list object of the primary keys. */ static void tcrdbqrypopmeta(RDBQRY *qry, TCLIST *res){ assert(qry && res); for(int i = tclistnum(res) - 1; i >= 0; i--){ int pksiz; const char *pkbuf = tclistval(res, i, &pksiz); if(pksiz >= 11 && pkbuf[0] == '\0' && pkbuf[1] == '\0'){ if(!memcmp(pkbuf + 2, "[[HINT]]\n", 9)){ int hsiz; char *hbuf = tclistpop(res, &hsiz); tcxstrcat(qry->hint, hbuf + 10, hsiz - 10); tcfree(hbuf); } else { break; } } else { break; } } } /* Search a server in parallel. `arg' specifies the artument structure of the query and the result. The return value is always `NULL'. */ static void *tcrdbparasearchworker(PARASEARCHARG *arg){ assert(arg); RDBQRY *qry = arg->qry; TCLIST *args = tclistdup(qry->args); tclistpush2(args, "get"); TCXSTR *xstr = tcxstrnew(); tcxstrcat2(xstr, "setlimit"); tcxstrcat(xstr, "\0", 1); tcxstrprintf(xstr, "%d", arg->max + arg->skip); tcxstrcat(xstr, "\0", 1); tcxstrprintf(xstr, "%d", 0); tclistpush(args, tcxstrptr(xstr), tcxstrsize(xstr)); tcxstrdel(xstr); arg->res = tcrdbmisc(qry->rdb, "search", RDBMONOULOG, args); tclistdel(args); return NULL; } /* Convert a string to a real number. `str' specifies the string. The return value is the real number. */ static long double tcrdbatof(const char *str){ assert(str); while(*str > '\0' && *str <= ' '){ str++; } int sign = 1; if(*str == '-'){ str++; sign = -1; } else if(*str == '+'){ str++; } if(tcstrifwm(str, "inf")) return HUGE_VALL * sign; if(tcstrifwm(str, "nan")) return nanl(""); long double num = 0; int col = 0; while(*str != '\0'){ if(*str < '0' || *str > '9') break; num = num * 10 + *str - '0'; str++; if(num > 0) col++; } if(*str == '.'){ str++; long double fract = 0.0; long double base = 10; while(col < RDBNUMCOLMAX && *str != '\0'){ if(*str < '0' || *str > '9') break; fract += (*str - '0') / base; str++; col++; base *= 10; } num += fract; } return num * sign; } /* Compare two sort records by string ascending. `a' specifies a key. `b' specifies of the other key. The return value is positive if the former is big, negative if the latter is big, 0 if both are equivalent. */ static int rdbcmpsortrecstrasc(const RDBSORTREC *a, const RDBSORTREC *b){ assert(a && b); if(!a->obuf){ if(!b->obuf) return 0; return 1; } if(!b->obuf){ if(!a->obuf) return 0; return -1; } return tccmplexical(a->obuf, a->osiz, b->obuf, b->osiz, NULL); } /* Compare two sort records by string descending. `a' specifies a key. `b' specifies of the other key. The return value is positive if the former is big, negative if the latter is big, 0 if both are equivalent. */ static int rdbcmpsortrecstrdesc(const RDBSORTREC *a, const RDBSORTREC *b){ assert(a && b); if(!a->obuf){ if(!b->obuf) return 0; return 1; } if(!b->obuf){ if(!a->obuf) return 0; return -1; } return -tccmplexical(a->obuf, a->osiz, b->obuf, b->osiz, NULL); } /* Compare two sort records by number ascending. `a' specifies a key. `b' specifies of the other key. The return value is positive if the former is big, negative if the latter is big, 0 if both are equivalent. */ static int rdbcmpsortrecnumasc(const RDBSORTREC *a, const RDBSORTREC *b){ assert(a && b); if(!a->obuf){ if(!b->obuf) return 0; return 1; } if(!b->obuf){ if(!a->obuf) return 0; return -1; } long double anum = tcrdbatof(a->obuf); long double bnum = tcrdbatof(b->obuf); if(anum < bnum) return -1; if(anum > bnum) return 1; return 0; } /* Compare two sort records by number descending. `a' specifies a key. `b' specifies of the other key. The return value is positive if the former is big, negative if the latter is big, 0 if both are equivalent. */ static int rdbcmpsortrecnumdesc(const RDBSORTREC *a, const RDBSORTREC *b){ assert(a && b); if(!a->obuf){ if(!b->obuf) return 0; return 1; } if(!b->obuf){ if(!a->obuf) return 0; return -1; } long double anum = tcrdbatof(a->obuf); long double bnum = tcrdbatof(b->obuf); if(anum < bnum) return 1; if(anum > bnum) return -1; return 0; } // END OF FILE tokyotyrant-1.1.40/tcrdb.h000066400000000000000000001161301133351147200154550ustar00rootroot00000000000000/************************************************************************************************* * The remote database API of Tokyo Tyrant * Copyright (C) 2006-2010 Mikio Hirabayashi * This file is part of Tokyo Tyrant. * Tokyo Tyrant is free software; you can redistribute it and/or modify it under the terms of * the GNU Lesser General Public License as published by the Free Software Foundation; either * version 2.1 of the License or any later version. Tokyo Tyrant 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 Lesser General Public * License for more details. * You should have received a copy of the GNU Lesser General Public License along with Tokyo * Tyrant; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA. *************************************************************************************************/ #ifndef _TCRDB_H /* duplication check */ #define _TCRDB_H #if defined(__cplusplus) #define __TCRDB_CLINKAGEBEGIN extern "C" { #define __TCRDB_CLINKAGEEND } #else #define __TCRDB_CLINKAGEBEGIN #define __TCRDB_CLINKAGEEND #endif __TCRDB_CLINKAGEBEGIN #include /************************************************************************************************* * API *************************************************************************************************/ typedef struct { /* type of structure for a remote database */ pthread_mutex_t mmtx; /* mutex for method */ pthread_key_t eckey; /* key for thread specific error code */ char *host; /* host name */ int port; /* port number */ char *expr; /* simple server expression */ int fd; /* file descriptor */ TTSOCK *sock; /* socket object */ double timeout; /* timeout */ int opts; /* options */ } TCRDB; enum { /* enumeration for error codes */ TTESUCCESS, /* success */ TTEINVALID, /* invalid operation */ TTENOHOST, /* host not found */ TTEREFUSED, /* connection refused */ TTESEND, /* send error */ TTERECV, /* recv error */ TTEKEEP, /* existing record */ TTENOREC, /* no record found */ TTEMISC = 9999 /* miscellaneous error */ }; enum { /* enumeration for tuning options */ RDBTRECON = 1 << 0 /* reconnect automatically */ }; enum { /* enumeration for scripting extension options */ RDBXOLCKREC = 1 << 0, /* record locking */ RDBXOLCKGLB = 1 << 1 /* global locking */ }; enum { /* enumeration for restore options */ RDBROCHKCON = 1 << 0 /* consistency checking */ }; enum { /* enumeration for miscellaneous operation options */ RDBMONOULOG = 1 << 0 /* omission of update log */ }; /* Get the message string corresponding to an error code. `ecode' specifies the error code. The return value is the message string of the error code. */ const char *tcrdberrmsg(int ecode); /* Create a remote database object. The return value is the new remote database object. */ TCRDB *tcrdbnew(void); /* Delete a remote database object. `rdb' specifies the remote database object. */ void tcrdbdel(TCRDB *rdb); /* Get the last happened error code of a remote database object. `rdb' specifies the remote database object. The return value is the last happened error code. The following error code is defined: `TTESUCCESS' for success, `TTEINVALID' for invalid operation, `TTENOHOST' for host not found, `TTEREFUSED' for connection refused, `TTESEND' for send error, `TTERECV' for recv error, `TTEKEEP' for existing record, `TTENOREC' for no record found, `TTEMISC' for miscellaneous error. */ int tcrdbecode(TCRDB *rdb); /* Set the tuning parameters of a hash database object. `rdb' specifies the remote database object. `timeout' specifies the timeout of each query in seconds. If it is not more than 0, the timeout is not specified. `opts' specifies options by bitwise-or: `RDBTRECON' specifies that the connection is recovered automatically when it is disconnected. If successful, the return value is true, else, it is false. Note that the tuning parameters should be set before the database is opened. */ bool tcrdbtune(TCRDB *rdb, double timeout, int opts); /* Open a remote database. `rdb' specifies the remote database object. `host' specifies the name or the address of the server. `port' specifies the port number. If it is not more than 0, UNIX domain socket is used and the path of the socket file is specified by the host parameter. If successful, the return value is true, else, it is false. */ bool tcrdbopen(TCRDB *rdb, const char *host, int port); /* Open a remote database with a simple server expression. `rdb' specifies the remote database object. `expr' specifies the simple server expression. It is composed of two substrings separated by ":". The former field specifies the name or the address of the server. The latter field specifies the port number. If the latter field is omitted, the default port number is specified. If successful, the return value is true, else, it is false. */ bool tcrdbopen2(TCRDB *rdb, const char *expr); /* Close a remote database object. `rdb' specifies the remote database object. If successful, the return value is true, else, it is false. */ bool tcrdbclose(TCRDB *rdb); /* Store a record into a remote database object. `rdb' specifies the remote database object. `kbuf' specifies the pointer to the region of the key. `ksiz' specifies the size of the region of the key. `vbuf' specifies the pointer to the region of the value. `vsiz' specifies the size of the region of the value. If successful, the return value is true, else, it is false. If a record with the same key exists in the database, it is overwritten. */ bool tcrdbput(TCRDB *rdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz); /* Store a string record into a remote object. `rdb' specifies the remote database object. `kstr' specifies the string of the key. `vstr' specifies the string of the value. If successful, the return value is true, else, it is false. If a record with the same key exists in the database, it is overwritten. */ bool tcrdbput2(TCRDB *rdb, const char *kstr, const char *vstr); /* Store a new record into a remote database object. `rdb' specifies the remote database object. `kbuf' specifies the pointer to the region of the key. `ksiz' specifies the size of the region of the key. `vbuf' specifies the pointer to the region of the value. `vsiz' specifies the size of the region of the value. If successful, the return value is true, else, it is false. If a record with the same key exists in the database, this function has no effect. */ bool tcrdbputkeep(TCRDB *rdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz); /* Store a new string record into a remote database object. `rdb' specifies the remote database object. `kstr' specifies the string of the key. `vstr' specifies the string of the value. If successful, the return value is true, else, it is false. If a record with the same key exists in the database, this function has no effect. */ bool tcrdbputkeep2(TCRDB *rdb, const char *kstr, const char *vstr); /* Concatenate a value at the end of the existing record in a remote database object. `rdb' specifies the remote database object. `kbuf' specifies the pointer to the region of the key. `ksiz' specifies the size of the region of the key. `vbuf' specifies the pointer to the region of the value. `vsiz' specifies the size of the region of the value. If successful, the return value is true, else, it is false. If there is no corresponding record, a new record is created. */ bool tcrdbputcat(TCRDB *rdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz); /* Concatenate a string value at the end of the existing record in a remote database object. `rdb' specifies the remote database object. `kstr' specifies the string of the key. `vstr' specifies the string of the value. If successful, the return value is true, else, it is false. If there is no corresponding record, a new record is created. */ bool tcrdbputcat2(TCRDB *rdb, const char *kstr, const char *vstr); /* Concatenate a value at the end of the existing record and shift it to the left. `rdb' specifies the remote database object. `kbuf' specifies the pointer to the region of the key. `ksiz' specifies the size of the region of the key. `vbuf' specifies the pointer to the region of the value. `vsiz' specifies the size of the region of the value. `width' specifies the width of the record. If successful, the return value is true, else, it is false. If there is no corresponding record, a new record is created. */ bool tcrdbputshl(TCRDB *rdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz, int width); /* Concatenate a string value at the end of the existing record and shift it to the left. `rdb' specifies the remote database object. `kstr' specifies the string of the key. `vstr' specifies the string of the value. `width' specifies the width of the record. If successful, the return value is true, else, it is false. If there is no corresponding record, a new record is created. */ bool tcrdbputshl2(TCRDB *rdb, const char *kstr, const char *vstr, int width); /* Store a record into a remote database object without response from the server. `rdb' specifies the remote database object. `kbuf' specifies the pointer to the region of the key. `ksiz' specifies the size of the region of the key. `vbuf' specifies the pointer to the region of the value. `vsiz' specifies the size of the region of the value. If successful, the return value is true, else, it is false. If a record with the same key exists in the database, it is overwritten. */ bool tcrdbputnr(TCRDB *rdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz); /* Store a string record into a remote object without response from the server. `rdb' specifies the remote database object. `kstr' specifies the string of the key. `vstr' specifies the string of the value. If successful, the return value is true, else, it is false. If a record with the same key exists in the database, it is overwritten. */ bool tcrdbputnr2(TCRDB *rdb, const char *kstr, const char *vstr); /* Remove a record of a remote database object. `rdb' specifies the remote database object. `kbuf' specifies the pointer to the region of the key. `ksiz' specifies the size of the region of the key. If successful, the return value is true, else, it is false. */ bool tcrdbout(TCRDB *rdb, const void *kbuf, int ksiz); /* Remove a string record of a remote database object. `rdb' specifies the remote database object. `kstr' specifies the string of the key. If successful, the return value is true, else, it is false. */ bool tcrdbout2(TCRDB *rdb, const char *kstr); /* Retrieve a record in a remote database object. `rdb' specifies the remote database object. `kbuf' specifies the pointer to the region of the key. `ksiz' specifies the size of the region of the key. `sp' specifies the pointer to the variable into which the size of the region of the return value is assigned. If successful, the return value is the pointer to the region of the value of the corresponding record. `NULL' is returned if no record corresponds. Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use. */ void *tcrdbget(TCRDB *rdb, const void *kbuf, int ksiz, int *sp); /* Retrieve a string record in a remote database object. `rdb' specifies the remote database object. `kstr' specifies the string of the key. If successful, the return value is the string of the value of the corresponding record. `NULL' is returned if no record corresponds. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use. */ char *tcrdbget2(TCRDB *rdb, const char *kstr); /* Retrieve records in a remote database object. `rdb' specifies the remote database object. `recs' specifies a map object containing the retrieval keys. As a result of this function, keys existing in the database have the corresponding values and keys not existing in the database are removed. If successful, the return value is true, else, it is false. */ bool tcrdbget3(TCRDB *rdb, TCMAP *recs); /* Get the size of the value of a record in a remote database object. `rdb' specifies the remote database object. `kbuf' specifies the pointer to the region of the key. `ksiz' specifies the size of the region of the key. If successful, the return value is the size of the value of the corresponding record, else, it is -1. */ int tcrdbvsiz(TCRDB *rdb, const void *kbuf, int ksiz); /* Get the size of the value of a string record in a remote database object. `rdb' specifies the remote database object. `kstr' specifies the string of the key. If successful, the return value is the size of the value of the corresponding record, else, it is -1. */ int tcrdbvsiz2(TCRDB *rdb, const char *kstr); /* Initialize the iterator of a remote database object. `rdb' specifies the remote database object. If successful, the return value is true, else, it is false. The iterator is used in order to access the key of every record stored in a database. */ bool tcrdbiterinit(TCRDB *rdb); /* Get the next key of the iterator of a remote database object. `rdb' specifies the remote database object. `sp' specifies the pointer to the variable into which the size of the region of the return value is assigned. If successful, the return value is the pointer to the region of the next key, else, it is `NULL'. `NULL' is returned when no record is to be get out of the iterator. Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use. The iterator can be updated by multiple connections and then it is not assured that every record is traversed. */ void *tcrdbiternext(TCRDB *rdb, int *sp); /* Get the next key string of the iterator of a remote database object. `rdb' specifies the remote database object. If successful, the return value is the string of the next key, else, it is `NULL'. `NULL' is returned when no record is to be get out of the iterator. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use. The iterator can be updated by multiple connections and then it is not assured that every record is traversed. */ char *tcrdbiternext2(TCRDB *rdb); /* Get forward matching keys in a remote database object. `rdb' specifies the remote database object. `pbuf' specifies the pointer to the region of the prefix. `psiz' specifies the size of the region of the prefix. `max' specifies the maximum number of keys to be fetched. If it is negative, no limit is specified. The return value is a list object of the corresponding keys. This function does never fail. It returns an empty list even if no key corresponds. Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use. */ TCLIST *tcrdbfwmkeys(TCRDB *rdb, const void *pbuf, int psiz, int max); /* Get forward matching string keys in a remote database object. `rdb' specifies the remote database object. `pstr' specifies the string of the prefix. `max' specifies the maximum number of keys to be fetched. If it is negative, no limit is specified. The return value is a list object of the corresponding keys. This function does never fail. It returns an empty list even if no key corresponds. Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use. */ TCLIST *tcrdbfwmkeys2(TCRDB *rdb, const char *pstr, int max); /* Add an integer to a record in a remote database object. `rdb' specifies the remote database object. `kbuf' specifies the pointer to the region of the key. `ksiz' specifies the size of the region of the key. `num' specifies the additional value. If successful, the return value is the summation value, else, it is `INT_MIN'. If the corresponding record exists, the value is treated as an integer and is added to. If no record corresponds, a new record of the additional value is stored. */ int tcrdbaddint(TCRDB *rdb, const void *kbuf, int ksiz, int num); /* Add a real number to a record in a remote database object. `rdb' specifies the remote database object. `kbuf' specifies the pointer to the region of the key. `ksiz' specifies the size of the region of the key. `num' specifies the additional value. If successful, the return value is the summation value, else, it is Not-a-Number. If the corresponding record exists, the value is treated as a real number and is added to. If no record corresponds, a new record of the additional value is stored. */ double tcrdbadddouble(TCRDB *rdb, const void *kbuf, int ksiz, double num); /* Call a function of the scripting language extension. `rdb' specifies the remote database object. `name' specifies the function name. `opts' specifies options by bitwise-or: `RDBXOLCKREC' for record locking, `RDBXOLCKGLB' for global locking. `kbuf' specifies the pointer to the region of the key. `ksiz' specifies the size of the region of the key. `vbuf' specifies the pointer to the region of the value. `vsiz' specifies the size of the region of the value. `sp' specifies the pointer to the variable into which the size of the region of the return value is assigned. If successful, the return value is the pointer to the region of the value of the response. `NULL' is returned on failure. Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use. */ void *tcrdbext(TCRDB *rdb, const char *name, int opts, const void *kbuf, int ksiz, const void *vbuf, int vsiz, int *sp); /* Call a function of the scripting language extension with string parameters. `rdb' specifies the remote database object. `name' specifies the function name. `opts' specifies options by bitwise-or: `RDBXOLCKREC' for record locking, `RDBXOLCKGLB' for global locking. `kstr' specifies the string of the key. `vstr' specifies the string of the value. If successful, the return value is the string of the value of the response. `NULL' is returned on failure. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use. */ char *tcrdbext2(TCRDB *rdb, const char *name, int opts, const char *kstr, const char *vstr); /* Synchronize updated contents of a remote database object with the file and the device. `rdb' specifies the remote database object. If successful, the return value is true, else, it is false. */ bool tcrdbsync(TCRDB *rdb); /* Optimize the storage of a remove database object. `rdb' specifies the remote database object. `params' specifies the string of the tuning parameters. If it is `NULL', it is not used. If successful, the return value is true, else, it is false. */ bool tcrdboptimize(TCRDB *rdb, const char *params); /* Remove all records of a remote database object. `rdb' specifies the remote database object. If successful, the return value is true, else, it is false. */ bool tcrdbvanish(TCRDB *rdb); /* Copy the database file of a remote database object. `rdb' specifies the remote database object. `path' specifies the path of the destination file. If it begins with `@', the trailing substring is executed as a command line. If successful, the return value is true, else, it is false. False is returned if the executed command returns non-zero code. The database file is assured to be kept synchronized and not modified while the copying or executing operation is in progress. So, this function is useful to create a backup file of the database file. */ bool tcrdbcopy(TCRDB *rdb, const char *path); /* Restore the database file of a remote database object from the update log. `rdb' specifies the remote database object. `path' specifies the path of the update log directory. `ts' specifies the beginning timestamp in microseconds. `opts' specifies options by bitwise-or: `RDBROCHKCON' for consistency checking. If successful, the return value is true, else, it is false. */ bool tcrdbrestore(TCRDB *rdb, const char *path, uint64_t ts, int opts); /* Set the replication master of a remote database object. `rdb' specifies the remote database object. `host' specifies the name or the address of the server. If it is `NULL', replication of the database is disabled. `port' specifies the port number. `ts' specifies the beginning timestamp in microseconds. `opts' specifies options by bitwise-or: `RDBROCHKCON' for consistency checking. If successful, the return value is true, else, it is false. */ bool tcrdbsetmst(TCRDB *rdb, const char *host, int port, uint64_t ts, int opts); /* Set the replication master of a remote database object with a simple server expression. `rdb' specifies the remote database object. `expr' specifies the simple server expression. It is composed of two substrings separated by ":". The former field specifies the name or the address of the server. The latter field specifies the port number. If the latter field is omitted, the default port number is specified. `ts' specifies the beginning timestamp in microseconds. `opts' specifies options by bitwise-or: `RDBROCHKCON' for consistency checking. If successful, the return value is true, else, it is false. */ bool tcrdbsetmst2(TCRDB *rdb, const char *expr, uint64_t ts, int opts); /* Get the simple server expression of an abstract database object. `rdb' specifies the remote database object. The return value is the simple server expression or `NULL' if the object does not connect to any database server. */ const char *tcrdbexpr(TCRDB *rdb); /* Get the number of records of a remote database object. `rdb' specifies the remote database object. The return value is the number of records or 0 if the object does not connect to any database server. */ uint64_t tcrdbrnum(TCRDB *rdb); /* Get the size of the database of a remote database object. `rdb' specifies the remote database object. The return value is the size of the database or 0 if the object does not connect to any database server. */ uint64_t tcrdbsize(TCRDB *rdb); /* Get the status string of the database of a remote database object. `rdb' specifies the remote database object. The return value is the status message of the database or `NULL' if the object does not connect to any database server. The message format is TSV. The first field of each line means the parameter name and the second field means the value. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use. */ char *tcrdbstat(TCRDB *rdb); /* Call a versatile function for miscellaneous operations of a remote database object. `rdb' specifies the remote database object. `name' specifies the name of the function. All databases support "putlist", "outlist", and "getlist". "putlist" is to store records. It receives keys and values one after the other, and returns an empty list. "outlist" is to remove records. It receives keys, and returns an empty list. "getlist" is to retrieve records. It receives keys, and returns keys and values of corresponding records one after the other. Table database supports "setindex", "search", and "genuid". `opts' specifies options by bitwise-or: `RDBMONOULOG' for omission of the update log. `args' specifies a list object containing arguments. If successful, the return value is a list object of the result. `NULL' is returned on failure. Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use. */ TCLIST *tcrdbmisc(TCRDB *rdb, const char *name, int opts, const TCLIST *args); /************************************************************************************************* * table extension *************************************************************************************************/ enum { /* enumeration for index types */ RDBITLEXICAL = TDBITLEXICAL, /* lexical string */ RDBITDECIMAL = TDBITDECIMAL, /* decimal string */ RDBITTOKEN = TDBITTOKEN, /* token inverted index */ RDBITQGRAM = TDBITQGRAM, /* q-gram inverted index */ RDBITOPT = TDBITOPT, /* optimize */ RDBITVOID = TDBITVOID, /* void */ RDBITKEEP = TDBITKEEP /* keep existing index */ }; typedef struct { /* type of structure for a query */ TCRDB *rdb; /* database object */ TCLIST *args; /* arguments for the method */ TCXSTR *hint; /* hint string */ } RDBQRY; enum { /* enumeration for query conditions */ RDBQCSTREQ = TDBQCSTREQ, /* string is equal to */ RDBQCSTRINC = TDBQCSTRINC, /* string is included in */ RDBQCSTRBW = TDBQCSTRBW, /* string begins with */ RDBQCSTREW = TDBQCSTREW, /* string ends with */ RDBQCSTRAND = TDBQCSTRAND, /* string includes all tokens in */ RDBQCSTROR = TDBQCSTROR, /* string includes at least one token in */ RDBQCSTROREQ = TDBQCSTROREQ, /* string is equal to at least one token in */ RDBQCSTRRX = TDBQCSTRRX, /* string matches regular expressions of */ RDBQCNUMEQ = TDBQCNUMEQ, /* number is equal to */ RDBQCNUMGT = TDBQCNUMGT, /* number is greater than */ RDBQCNUMGE = TDBQCNUMGE, /* number is greater than or equal to */ RDBQCNUMLT = TDBQCNUMLT, /* number is less than */ RDBQCNUMLE = TDBQCNUMLE, /* number is less than or equal to */ RDBQCNUMBT = TDBQCNUMBT, /* number is between two tokens of */ RDBQCNUMOREQ = TDBQCNUMOREQ, /* number is equal to at least one token in */ RDBQCFTSPH = TDBQCFTSPH, /* full-text search with the phrase of */ RDBQCFTSAND = TDBQCFTSAND, /* full-text search with all tokens in */ RDBQCFTSOR = TDBQCFTSOR, /* full-text search with at least one token in */ RDBQCFTSEX = TDBQCFTSEX, /* full-text search with the compound expression of */ RDBQCNEGATE = TDBQCNEGATE, /* negation flag */ RDBQCNOIDX = TDBQCNOIDX /* no index flag */ }; enum { /* enumeration for order types */ RDBQOSTRASC = TDBQOSTRASC, /* string ascending */ RDBQOSTRDESC = TDBQOSTRDESC, /* string descending */ RDBQONUMASC = TDBQONUMASC, /* number ascending */ RDBQONUMDESC = TDBQONUMDESC /* number descending */ }; enum { /* enumeration for set operation types */ RDBMSUNION = TDBMSUNION, /* union */ RDBMSISECT = TDBMSISECT, /* intersection */ RDBMSDIFF = TDBMSDIFF /* difference */ }; /* Store a record into a remote database object. `rdb' specifies the remote database object. `pkbuf' specifies the pointer to the region of the primary key. `pksiz' specifies the size of the region of the primary key. `cols' specifies a map object containing columns. If successful, the return value is true, else, it is false. If a record with the same key exists in the database, it is overwritten. */ bool tcrdbtblput(TCRDB *rdb, const void *pkbuf, int pksiz, TCMAP *cols); /* Store a new record into a remote database object. `rdb' specifies the remote database object. `pkbuf' specifies the pointer to the region of the primary key. `pksiz' specifies the size of the region of the primary key. `cols' specifies a map object containing columns. If successful, the return value is true, else, it is false. If a record with the same key exists in the database, this function has no effect. */ bool tcrdbtblputkeep(TCRDB *rdb, const void *pkbuf, int pksiz, TCMAP *cols); /* Concatenate columns of the existing record in a remote database object. `rdb' specifies the remote database object. `pkbuf' specifies the pointer to the region of the primary key. `pksiz' specifies the size of the region of the primary key. `cols' specifies a map object containing columns. If successful, the return value is true, else, it is false. If there is no corresponding record, a new record is created. */ bool tcrdbtblputcat(TCRDB *rdb, const void *pkbuf, int pksiz, TCMAP *cols); /* Remove a record of a remote database object. `rdb' specifies the remote database object. `pkbuf' specifies the pointer to the region of the primary key. `pksiz' specifies the size of the region of the primary key. If successful, the return value is true, else, it is false. */ bool tcrdbtblout(TCRDB *rdb, const void *pkbuf, int pksiz); /* Retrieve a record in a remote database object. `rdb' specifies the remote database object. `pkbuf' specifies the pointer to the region of the primary key. `pksiz' specifies the size of the region of the primary key. If successful, the return value is a map object of the columns of the corresponding record. `NULL' is returned if no record corresponds. Because the object of the return value is created with the function `tcmapnew', it should be deleted with the function `tcmapdel' when it is no longer in use. */ TCMAP *tcrdbtblget(TCRDB *rdb, const void *pkbuf, int pksiz); /* Set a column index to a remote database object. `rdb' specifies the remote database object. `name' specifies the name of a column. If the name of an existing index is specified, the index is rebuilt. An empty string means the primary key. `type' specifies the index type: `RDBITLEXICAL' for lexical string, `RDBITDECIMAL' for decimal string, `RDBITTOKEN' for token inverted index, `RDBITQGRAM' for q-gram inverted index. If it is `RDBITOPT', the index is optimized. If it is `RDBITVOID', the index is removed. If `RDBITKEEP' is added by bitwise-or and the index exists, this function merely returns failure. If successful, the return value is true, else, it is false. */ bool tcrdbtblsetindex(TCRDB *rdb, const char *name, int type); /* Generate a unique ID number of a remote database object. `rdb' specifies the remote database object. The return value is the new unique ID number or -1 on failure. */ int64_t tcrdbtblgenuid(TCRDB *rdb); /* Create a query object. `rdb' specifies the remote database object. The return value is the new query object. */ RDBQRY *tcrdbqrynew(TCRDB *rdb); /* Delete a query object. `qry' specifies the query object. */ void tcrdbqrydel(RDBQRY *qry); /* Add a narrowing condition to a query object. `qry' specifies the query object. `name' specifies the name of a column. An empty string means the primary key. `op' specifies an operation type: `RDBQCSTREQ' for string which is equal to the expression, `RDBQCSTRINC' for string which is included in the expression, `RDBQCSTRBW' for string which begins with the expression, `RDBQCSTREW' for string which ends with the expression, `RDBQCSTRAND' for string which includes all tokens in the expression, `RDBQCSTROR' for string which includes at least one token in the expression, `RDBQCSTROREQ' for string which is equal to at least one token in the expression, `RDBQCSTRRX' for string which matches regular expressions of the expression, `RDBQCNUMEQ' for number which is equal to the expression, `RDBQCNUMGT' for number which is greater than the expression, `RDBQCNUMGE' for number which is greater than or equal to the expression, `RDBQCNUMLT' for number which is less than the expression, `RDBQCNUMLE' for number which is less than or equal to the expression, `RDBQCNUMBT' for number which is between two tokens of the expression, `RDBQCNUMOREQ' for number which is equal to at least one token in the expression, `RDBQCFTSPH' for full-text search with the phrase of the expression, `RDBQCFTSAND' for full-text search with all tokens in the expression, `RDBQCFTSOR' for full-text search with at least one token in the expression, `RDBQCFTSEX' for full-text search with the compound expression. All operations can be flagged by bitwise-or: `RDBQCNEGATE' for negation, `RDBQCNOIDX' for using no index. `expr' specifies an operand exression. */ void tcrdbqryaddcond(RDBQRY *qry, const char *name, int op, const char *expr); /* Set the order of a query object. `qry' specifies the query object. `name' specifies the name of a column. An empty string means the primary key. `type' specifies the order type: `RDBQOSTRASC' for string ascending, `RDBQOSTRDESC' for string descending, `RDBQONUMASC' for number ascending, `RDBQONUMDESC' for number descending. */ void tcrdbqrysetorder(RDBQRY *qry, const char *name, int type); /* Set the limit number of records of the result of a query object. `qry' specifies the query object. `max' specifies the maximum number of records of the result. If it is negative, no limit is specified. `skip' specifies the number of skipped records of the result. If it is not more than 0, no record is skipped. */ void tcrdbqrysetlimit(RDBQRY *qry, int max, int skip); /* Execute the search of a query object. `qry' specifies the query object. The return value is a list object of the primary keys of the corresponding records. This function does never fail. It returns an empty list even if no record corresponds. Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use. */ TCLIST *tcrdbqrysearch(RDBQRY *qry); /* Remove each record corresponding to a query object. `qry' specifies the query object of the database. If successful, the return value is true, else, it is false. */ bool tcrdbqrysearchout(RDBQRY *qry); /* Get records corresponding to the search of a query object. `qry' specifies the query object. The return value is a list object of zero separated columns of the corresponding records. This function does never fail. It returns an empty list even if no record corresponds. Each element of the list can be treated with the function `tcrdbqryrescols'. Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use. */ TCLIST *tcrdbqrysearchget(RDBQRY *qry); /* Get columns of a record in a search result. `res' specifies a list of zero separated columns of the search result. `index' the index of a element of the search result. The return value is a map object containing columns. Because the object of the return value is created with the function `tcmapnew', it should be deleted with the function `tcmapdel' when it is no longer in use. */ TCMAP *tcrdbqryrescols(TCLIST *res, int index); /* Get the count of corresponding records of a query object. `qry' specifies the query object. The return value is the count of corresponding records or 0 on failure. */ int tcrdbqrysearchcount(RDBQRY *qry); /* Get the hint string of a query object. `qry' specifies the query object. The return value is the hint string. This function should be called after the query execution by `tcrdbqrysearch' and so on. The region of the return value is overwritten when this function is called again. */ const char *tcrdbqryhint(RDBQRY *qry); /* Retrieve records with multiple query objects and get the set of the result. `qrys' specifies an array of the query objects. `num' specifies the number of elements of the array. `type' specifies a set operation type: `RDBMSUNION' for the union set, `RDBMSISECT' for the intersection set, `RDBMSDIFF' for the difference set. The return value is a list object of the primary keys of the corresponding records. This function does never fail. It returns an empty list even if no record corresponds. If the first query object has the order setting, the result array is sorted by the order. Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use. */ TCLIST *tcrdbmetasearch(RDBQRY **qrys, int num, int type); /* Search for multiple servers in parallel. `qrys' specifies an array of the query objects. `num' specifies the number of elements of the array. The return value is a list object of zero separated columns of the corresponding records. This function does never fail. It returns an empty list even if no record corresponds. Each element of the list can be treated with the function `tcrdbqryrescols'. Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use. */ TCLIST *tcrdbparasearch(RDBQRY **qrys, int num); /************************************************************************************************* * features for experts *************************************************************************************************/ /* Set the error code of a remote database object. `rdb' specifies the remote database object. `ecode' specifies the error code. */ void tcrdbsetecode(TCRDB *rdb, int ecode); __TCRDB_CLINKAGEEND #endif /* duplication check */ /* END OF FILE */ tokyotyrant-1.1.40/tcrmgr.c000066400000000000000000001171441133351147200156560ustar00rootroot00000000000000/************************************************************************************************* * The command line utility of the remote database API * Copyright (C) 2007-2010 Mikio Hirabayashi * This file is part of Tokyo Tyrant. * Tokyo Tyrant is free software; you can redistribute it and/or modify it under the terms of * the GNU Lesser General Public License as published by the Free Software Foundation; either * version 2.1 of the License or any later version. Tokyo Tyrant 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 Lesser General Public * License for more details. * You should have received a copy of the GNU Lesser General Public License along with Tokyo * Tyrant; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA. *************************************************************************************************/ #include #include #include "myconf.h" #define REQHEADMAX 32 // maximum number of request headers of HTTP #define MINIBNUM 31 // bucket number of map for trivial use /* global variables */ const char *g_progname; // program name /* function prototypes */ int main(int argc, char **argv); static void usage(void); static void printerr(TCRDB *rdb); static int sepstrtochr(const char *str); static char *strtozsv(const char *str, int sep, int *sp); static int printdata(const char *ptr, int size, bool px, int sep); static char *mygetline(FILE *ifp); static bool myopen(TCRDB *rdb, const char *host, int port); static bool mysetmst(TCRDB *rdb, const char *host, int port, uint64_t ts, int opts); static int runinform(int argc, char **argv); static int runput(int argc, char **argv); static int runout(int argc, char **argv); static int runget(int argc, char **argv); static int runmget(int argc, char **argv); static int runlist(int argc, char **argv); static int runext(int argc, char **argv); static int runsync(int argc, char **argv); static int runoptimize(int argc, char **argv); static int runvanish(int argc, char **argv); static int runcopy(int argc, char **argv); static int runmisc(int argc, char **argv); static int runimporttsv(int argc, char **argv); static int runrestore(int argc, char **argv); static int runsetmst(int argc, char **argv); static int runrepl(int argc, char **argv); static int runhttp(int argc, char **argv); static int runversion(int argc, char **argv); static int procinform(const char *host, int port, bool st); static int procput(const char *host, int port, const char *kbuf, int ksiz, const char *vbuf, int vsiz, int dmode, int shlw); static int procout(const char *host, int port, const char *kbuf, int ksiz); static int procget(const char *host, int port, const char *kbuf, int ksiz, int sep, bool px, bool pz); static int procmget(const char *host, int port, const TCLIST *keys, int sep, bool px); static int proclist(const char *host, int port, int sep, int max, bool pv, bool px, const char *fmstr); static int procext(const char *host, int port, const char *func, int opts, const char *kbuf, int ksiz, const char *vbuf, int vsiz, int sep, bool px, bool pz); static int procsync(const char *host, int port); static int procoptimize(const char *host, int port, const char *params); static int procvanish(const char *host, int port); static int proccopy(const char *host, int port, const char *dpath); static int procmisc(const char *host, int port, const char *func, int opts, const TCLIST *args, int sep, bool px); static int procimporttsv(const char *host, int port, const char *file, bool nr, bool sc, int sep); static int procrestore(const char *host, int port, const char *upath, uint64_t ts, int opts); static int procsetmst(const char *host, int port, const char *mhost, int mport, uint64_t ts, int opts); static int procrepl(const char *host, int port, uint64_t ts, uint32_t sid, bool ph); static int prochttp(const char *url, TCMAP *hmap, bool ih); static int procversion(void); /* main routine */ int main(int argc, char **argv){ g_progname = argv[0]; signal(SIGPIPE, SIG_IGN); if(argc < 2) usage(); int rv = 0; if(!strcmp(argv[1], "inform")){ rv = runinform(argc, argv); } else if(!strcmp(argv[1], "put")){ rv = runput(argc, argv); } else if(!strcmp(argv[1], "out")){ rv = runout(argc, argv); } else if(!strcmp(argv[1], "get")){ rv = runget(argc, argv); } else if(!strcmp(argv[1], "mget")){ rv = runmget(argc, argv); } else if(!strcmp(argv[1], "list")){ rv = runlist(argc, argv); } else if(!strcmp(argv[1], "ext")){ rv = runext(argc, argv); } else if(!strcmp(argv[1], "sync")){ rv = runsync(argc, argv); } else if(!strcmp(argv[1], "optimize")){ rv = runoptimize(argc, argv); } else if(!strcmp(argv[1], "vanish")){ rv = runvanish(argc, argv); } else if(!strcmp(argv[1], "copy")){ rv = runcopy(argc, argv); } else if(!strcmp(argv[1], "misc")){ rv = runmisc(argc, argv); } else if(!strcmp(argv[1], "importtsv")){ rv = runimporttsv(argc, argv); } else if(!strcmp(argv[1], "restore")){ rv = runrestore(argc, argv); } else if(!strcmp(argv[1], "setmst")){ rv = runsetmst(argc, argv); } else if(!strcmp(argv[1], "repl")){ rv = runrepl(argc, argv); } else if(!strcmp(argv[1], "http")){ rv = runhttp(argc, argv); } else if(!strcmp(argv[1], "version") || !strcmp(argv[1], "--version")){ rv = runversion(argc, argv); } else { usage(); } return rv; } /* print the usage and exit */ static void usage(void){ fprintf(stderr, "%s: the command line utility of the remote database API\n", g_progname); fprintf(stderr, "\n"); fprintf(stderr, "usage:\n"); fprintf(stderr, " %s inform [-port num] [-st] host\n", g_progname); fprintf(stderr, " %s put [-port num] [-sx] [-sep chr] [-dk|-dc|-dai|-dad] [-ds num]" " host key value\n", g_progname); fprintf(stderr, " %s out [-port num] [-sx] [-sep chr] host key\n", g_progname); fprintf(stderr, " %s get [-port num] [-sx] [-sep chr] [-px] [-pz] host key\n", g_progname); fprintf(stderr, " %s mget [-port num] [-sx] [-sep chr] [-px] host [key...]\n", g_progname); fprintf(stderr, " %s list [-port num] [-sep chr] [-m num] [-pv] [-px] [-fm str] host\n", g_progname); fprintf(stderr, " %s ext [-port num] [-xlr|-xlg] [-sx] [-sep chr] [-px] host func" " [key [value]]\n", g_progname); fprintf(stderr, " %s sync [-port num] host\n", g_progname); fprintf(stderr, " %s optimize [-port num] host [params]\n", g_progname); fprintf(stderr, " %s vanish [-port num] host\n", g_progname); fprintf(stderr, " %s copy [-port num] host dpath\n", g_progname); fprintf(stderr, " %s misc [-port num] [-mnu] [-sx] [-sep chr] [-px] host func [arg...]\n", g_progname); fprintf(stderr, " %s importtsv [-port num] [-nr] [-sc] [-sep chr] host [file]\n", g_progname); fprintf(stderr, " %s restore [-port num] [-ts num] [-rcc] host upath\n", g_progname); fprintf(stderr, " %s setmst [-port num] [-mport num] [-ts num] [-rcc] host [mhost]\n", g_progname); fprintf(stderr, " %s repl [-port num] [-ts num] [-sid num] [-ph] host\n", g_progname); fprintf(stderr, " %s http [-ah name value] [-ih] url\n", g_progname); fprintf(stderr, " %s version\n", g_progname); fprintf(stderr, "\n"); exit(1); } /* print error information */ static void printerr(TCRDB *rdb){ int ecode = tcrdbecode(rdb); fprintf(stderr, "%s: error: %d: %s\n", g_progname, ecode, tcrdberrmsg(ecode)); } /* get the character of separation string */ static int sepstrtochr(const char *str){ if(!strcmp(str, "\\t")) return '\t'; if(!strcmp(str, "\\r")) return '\r'; if(!strcmp(str, "\\n")) return '\n'; return *(unsigned char *)str; } /* encode a string as a zero separaterd string */ static char *strtozsv(const char *str, int sep, int *sp){ int size = strlen(str); char *buf = tcmemdup(str, size); for(int i = 0; i < size; i++){ if(buf[i] == sep) buf[i] = '\0'; } *sp = size; return buf; } /* print record data */ static int printdata(const char *ptr, int size, bool px, int sep){ int len = 0; while(size-- > 0){ if(px){ if(len > 0) putchar(' '); len += printf("%02X", *(unsigned char *)ptr); } else if(sep > 0){ if(*ptr == '\0'){ putchar(sep); } else { putchar(*ptr); } len++; } else { putchar(*ptr); len++; } ptr++; } return len; } /* read a line from a file descriptor */ static char *mygetline(FILE *ifp){ int len = 0; int blen = 1024; char *buf = tcmalloc(blen); bool end = true; int c; while((c = fgetc(ifp)) != EOF){ end = false; if(c == '\0') continue; if(blen <= len){ blen *= 2; buf = tcrealloc(buf, blen + 1); } if(c == '\n' || c == '\r') c = '\0'; buf[len++] = c; if(c == '\0') break; } if(end){ tcfree(buf); return NULL; } buf[len] = '\0'; return buf; } /* open the remote database */ static bool myopen(TCRDB *rdb, const char *host, int port){ bool err = false; if(strchr(host, ':') || strchr(host, '#')){ if(!tcrdbopen2(rdb, host)) err = true; } else { if(!tcrdbopen(rdb, host, port)) err = true; } return !err; } /* set the replication master */ static bool mysetmst(TCRDB *rdb, const char *host, int port, uint64_t ts, int opts){ bool err = false; if(strchr(host, ':')){ if(!tcrdbsetmst2(rdb, host, ts, opts)) err = true; } else { if(!tcrdbsetmst(rdb, host, port, ts, opts)) err = true; } return !err; } /* parse arguments of inform command */ static int runinform(int argc, char **argv){ char *host = NULL; int port = TTDEFPORT; bool st = false; for(int i = 2; i < argc; i++){ if(!host && argv[i][0] == '-'){ if(!strcmp(argv[i], "-port")){ if(++i >= argc) usage(); port = tcatoi(argv[i]); } else if(!strcmp(argv[i], "-st")){ st = true; } else { usage(); } } else if(!host){ host = argv[i]; } else { usage(); } } if(!host) usage(); int rv = procinform(host, port, st); return rv; } /* parse arguments of put command */ static int runput(int argc, char **argv){ char *host = NULL; char *key = NULL; char *value = NULL; int port = TTDEFPORT; int dmode = 0; int shlw = -1; bool sx = false; int sep = -1; for(int i = 2; i < argc; i++){ if(!host && argv[i][0] == '-'){ if(!strcmp(argv[i], "-port")){ if(++i >= argc) usage(); port = tcatoi(argv[i]); } else if(!strcmp(argv[i], "-dk")){ dmode = -1; } else if(!strcmp(argv[i], "-dc")){ dmode = 1; } else if(!strcmp(argv[i], "-dai")){ dmode = 10; } else if(!strcmp(argv[i], "-dad")){ dmode = 11; } else if(!strcmp(argv[i], "-ds")){ if(++i >= argc) usage(); shlw = tcatoi(argv[i]); } else if(!strcmp(argv[i], "-sx")){ sx = true; } else if(!strcmp(argv[i], "-sep")){ if(++i >= argc) usage(); sep = sepstrtochr(argv[i]); } else { usage(); } } else if(!host){ host = argv[i]; } else if(!key){ key = argv[i]; } else if(!value){ value = argv[i]; } else { usage(); } } if(!host || !key || !value) usage(); int ksiz, vsiz; char *kbuf, *vbuf; if(sx){ kbuf = tchexdecode(key, &ksiz); vbuf = tchexdecode(value, &vsiz); } else if(sep > 0){ kbuf = strtozsv(key, sep, &ksiz); vbuf = strtozsv(value, sep, &vsiz); } else { ksiz = strlen(key); kbuf = tcmemdup(key, ksiz); vsiz = strlen(value); vbuf = tcmemdup(value, vsiz); } int rv = procput(host, port, kbuf, ksiz, vbuf, vsiz, dmode, shlw); tcfree(vbuf); tcfree(kbuf); return rv; } /* parse arguments of out command */ static int runout(int argc, char **argv){ char *host = NULL; char *key = NULL; int port = TTDEFPORT; bool sx = false; int sep = -1; for(int i = 2; i < argc; i++){ if(!host && argv[i][0] == '-'){ if(!strcmp(argv[i], "-port")){ if(++i >= argc) usage(); port = tcatoi(argv[i]); } else if(!strcmp(argv[i], "-sx")){ sx = true; } else if(!strcmp(argv[i], "-sep")){ if(++i >= argc) usage(); sep = sepstrtochr(argv[i]); } else { usage(); } } else if(!host){ host = argv[i]; } else if(!key){ key = argv[i]; } else { usage(); } } if(!host || !key) usage(); int ksiz; char *kbuf; if(sx){ kbuf = tchexdecode(key, &ksiz); } else if(sep > 0){ kbuf = strtozsv(key, sep, &ksiz); } else { ksiz = strlen(key); kbuf = tcmemdup(key, ksiz); } int rv = procout(host, port, kbuf, ksiz); tcfree(kbuf); return rv; } /* parse arguments of get command */ static int runget(int argc, char **argv){ char *host = NULL; char *key = NULL; int port = TTDEFPORT; bool sx = false; int sep = -1; bool px = false; bool pz = false; for(int i = 2; i < argc; i++){ if(!host && argv[i][0] == '-'){ if(!strcmp(argv[i], "-port")){ if(++i >= argc) usage(); port = tcatoi(argv[i]); } else if(!strcmp(argv[i], "-sx")){ sx = true; } else if(!strcmp(argv[i], "-sep")){ if(++i >= argc) usage(); sep = sepstrtochr(argv[i]); } else if(!strcmp(argv[i], "-px")){ px = true; } else if(!strcmp(argv[i], "-pz")){ pz = true; } else { usage(); } } else if(!host){ host = argv[i]; } else if(!key){ key = argv[i]; } else { usage(); } } if(!host || !key) usage(); int ksiz; char *kbuf; if(sx){ kbuf = tchexdecode(key, &ksiz); } else if(sep > 0){ kbuf = strtozsv(key, sep, &ksiz); } else { ksiz = strlen(key); kbuf = tcmemdup(key, ksiz); } int rv = procget(host, port, kbuf, ksiz, sep, px, pz); tcfree(kbuf); return rv; } /* parse arguments of mget command */ static int runmget(int argc, char **argv){ char *host = NULL; TCLIST *keys = tcmpoollistnew(tcmpoolglobal()); int port = TTDEFPORT; bool sx = false; int sep = -1; bool px = false; for(int i = 2; i < argc; i++){ if(!host && argv[i][0] == '-'){ if(!strcmp(argv[i], "-port")){ if(++i >= argc) usage(); port = tcatoi(argv[i]); } else if(!strcmp(argv[i], "-sx")){ sx = true; } else if(!strcmp(argv[i], "-sep")){ if(++i >= argc) usage(); sep = sepstrtochr(argv[i]); } else if(!strcmp(argv[i], "-px")){ px = true; } else { usage(); } } else if(!host){ host = argv[i]; } else { tclistpush2(keys, argv[i]); } } if(!host) usage(); if(sx){ for(int i = 0; i < tclistnum(keys); i++){ int ksiz; char *kbuf = tchexdecode(tclistval2(keys, i), &ksiz); tclistover(keys, i, kbuf, ksiz); tcfree(kbuf); } } else if(sep > 0){ for(int i = 0; i < tclistnum(keys); i++){ int ksiz; char *kbuf = strtozsv(tclistval2(keys, i), sep, &ksiz); tclistover(keys, i, kbuf, ksiz); tcfree(kbuf); } } int rv = procmget(host, port, keys, sep, px); return rv; } /* parse arguments of list command */ static int runlist(int argc, char **argv){ char *host = NULL; int port = TTDEFPORT; int sep = -1; int max = -1; bool pv = false; bool px = false; char *fmstr = NULL; for(int i = 2; i < argc; i++){ if(!host && argv[i][0] == '-'){ if(!strcmp(argv[i], "-port")){ if(++i >= argc) usage(); port = tcatoi(argv[i]); } else if(!strcmp(argv[i], "-sep")){ if(++i >= argc) usage(); sep = sepstrtochr(argv[i]); } else if(!strcmp(argv[i], "-m")){ if(++i >= argc) usage(); max = tcatoi(argv[i]); } else if(!strcmp(argv[i], "-pv")){ pv = true; } else if(!strcmp(argv[i], "-px")){ px = true; } else if(!strcmp(argv[i], "-fm")){ if(++i >= argc) usage(); fmstr = argv[i]; } else { usage(); } } else if(!host){ host = argv[i]; } else { usage(); } } if(!host) usage(); int rv = proclist(host, port, sep, max, pv, px, fmstr); return rv; } /* parse arguments of ext command */ static int runext(int argc, char **argv){ char *host = NULL; char *func = NULL; char *key = NULL; char *value = NULL; int port = TTDEFPORT; int opts = 0; bool sx = false; int sep = -1; bool px = false; bool pz = false; for(int i = 2; i < argc; i++){ if(!host && argv[i][0] == '-'){ if(!strcmp(argv[i], "-port")){ if(++i >= argc) usage(); port = tcatoi(argv[i]); } else if(!strcmp(argv[i], "-xlr")){ opts |= RDBXOLCKREC; } else if(!strcmp(argv[i], "-xlg")){ opts |= RDBXOLCKGLB; } else if(!strcmp(argv[i], "-sx")){ sx = true; } else if(!strcmp(argv[i], "-sep")){ if(++i >= argc) usage(); sep = sepstrtochr(argv[i]); } else if(!strcmp(argv[i], "-px")){ px = true; } else if(!strcmp(argv[i], "-pz")){ pz = true; } else { usage(); } } else if(!host){ host = argv[i]; } else if(!func){ func = argv[i]; } else if(!key){ key = argv[i]; } else if(!value){ value = argv[i]; } else { usage(); } } if(!host || !func) usage(); if(!key) key = ""; if(!value) value = ""; int ksiz, vsiz; char *kbuf, *vbuf; if(sx){ kbuf = tchexdecode(key, &ksiz); vbuf = tchexdecode(value, &vsiz); } else if(sep > 0){ kbuf = strtozsv(key, sep, &ksiz); vbuf = strtozsv(value, sep, &vsiz); } else { ksiz = strlen(key); kbuf = tcmemdup(key, ksiz); vsiz = strlen(value); vbuf = tcmemdup(value, vsiz); } int rv = procext(host, port, func, opts, kbuf, ksiz, vbuf, vsiz, sep, px, pz); tcfree(vbuf); tcfree(kbuf); return rv; } /* parse arguments of sync command */ static int runsync(int argc, char **argv){ char *host = NULL; int port = TTDEFPORT; for(int i = 2; i < argc; i++){ if(!host && argv[i][0] == '-'){ if(!strcmp(argv[i], "-port")){ if(++i >= argc) usage(); port = tcatoi(argv[i]); } else { usage(); } } else if(!host){ host = argv[i]; } else { usage(); } } if(!host) usage(); int rv = procsync(host, port); return rv; } /* parse arguments of optimize command */ static int runoptimize(int argc, char **argv){ char *host = NULL; char *params = NULL; int port = TTDEFPORT; for(int i = 2; i < argc; i++){ if(!host && argv[i][0] == '-'){ if(!strcmp(argv[i], "-port")){ if(++i >= argc) usage(); port = tcatoi(argv[i]); } else { usage(); } } else if(!host){ host = argv[i]; } else if(!params){ params = argv[i]; } else { usage(); } } if(!host) usage(); int rv = procoptimize(host, port, params); return rv; } /* parse arguments of vanish command */ static int runvanish(int argc, char **argv){ char *host = NULL; int port = TTDEFPORT; for(int i = 2; i < argc; i++){ if(!host && argv[i][0] == '-'){ if(!strcmp(argv[i], "-port")){ if(++i >= argc) usage(); port = tcatoi(argv[i]); } else { usage(); } } else if(!host){ host = argv[i]; } else { usage(); } } if(!host) usage(); int rv = procvanish(host, port); return rv; } /* parse arguments of copy command */ static int runcopy(int argc, char **argv){ char *host = NULL; char *dpath = NULL; int port = TTDEFPORT; for(int i = 2; i < argc; i++){ if(!host && argv[i][0] == '-'){ if(!strcmp(argv[i], "-port")){ if(++i >= argc) usage(); port = tcatoi(argv[i]); } else { usage(); } } else if(!host){ host = argv[i]; } else if(!dpath){ dpath = argv[i]; } else { usage(); } } if(!host || !dpath) usage(); int rv = proccopy(host, port, dpath); return rv; } /* parse arguments of misc command */ static int runmisc(int argc, char **argv){ char *host = NULL; char *func = NULL; TCLIST *args = tcmpoollistnew(tcmpoolglobal()); int port = TTDEFPORT; int opts = 0; bool sx = false; int sep = -1; bool px = false; for(int i = 2; i < argc; i++){ if(!host && argv[i][0] == '-'){ if(!strcmp(argv[i], "-port")){ if(++i >= argc) usage(); port = tcatoi(argv[i]); } else if(!strcmp(argv[i], "-mnu")){ opts |= RDBMONOULOG; } else if(!strcmp(argv[i], "-sx")){ sx = true; } else if(!strcmp(argv[i], "-sep")){ if(++i >= argc) usage(); sep = sepstrtochr(argv[i]); } else if(!strcmp(argv[i], "-px")){ px = true; } else { usage(); } } else if(!host){ host = argv[i]; } else if(!func){ func = argv[i]; } else { if(sx){ int size; char *buf = tchexdecode(argv[i], &size); tclistpush(args, buf, size); tcfree(buf); } else if(sep > 0){ int size; char *buf = strtozsv(argv[i], sep, &size); tclistpush(args, buf, size); tcfree(buf); } else { tclistpush2(args, argv[i]); } } } if(!host || !func) usage(); int rv = procmisc(host, port, func, opts, args, sep, px); return rv; } /* parse arguments of importtsv command */ static int runimporttsv(int argc, char **argv){ char *host = NULL; char *file = NULL; int port = TTDEFPORT; bool nr = false; bool sc = false; int sep = -1; for(int i = 2; i < argc; i++){ if(!host && argv[i][0] == '-'){ if(!strcmp(argv[i], "-port")){ if(++i >= argc) usage(); port = tcatoi(argv[i]); } else if(!strcmp(argv[i], "-nr")){ nr = true; } else if(!strcmp(argv[i], "-sc")){ sc = true; } else if(!strcmp(argv[i], "-sep")){ if(++i >= argc) usage(); sep = sepstrtochr(argv[i]); } else { usage(); } } else if(!host){ host = argv[i]; } else if(!file){ file = argv[i]; } else { usage(); } } if(!host) usage(); int rv = procimporttsv(host, port, file, nr, sc, sep); return rv; } /* parse arguments of restore command */ static int runrestore(int argc, char **argv){ char *host = NULL; char *upath = NULL; int port = TTDEFPORT; uint64_t ts = 0; int opts = 0; for(int i = 2; i < argc; i++){ if(!host && argv[i][0] == '-'){ if(!strcmp(argv[i], "-port")){ if(++i >= argc) usage(); port = tcatoi(argv[i]); } else if(!strcmp(argv[i], "-ts")){ if(++i >= argc) usage(); ts = ttstrtots(argv[i]); } else if(!strcmp(argv[i], "-rcc")){ opts |= RDBROCHKCON; } else { usage(); } } else if(!host){ host = argv[i]; } else if(!upath){ upath = argv[i]; } else { usage(); } } if(!host || !upath) usage(); int rv = procrestore(host, port, upath, ts, opts); return rv; } /* parse arguments of setmst command */ static int runsetmst(int argc, char **argv){ char *host = NULL; char *mhost = NULL; int port = TTDEFPORT; int mport = TTDEFPORT; uint64_t ts = 0; int opts = 0; for(int i = 2; i < argc; i++){ if(!host && argv[i][0] == '-'){ if(!strcmp(argv[i], "-port")){ if(++i >= argc) usage(); port = tcatoi(argv[i]); } else if(!strcmp(argv[i], "-mport")){ if(++i >= argc) usage(); mport = tcatoi(argv[i]); } else if(!strcmp(argv[i], "-ts")){ if(++i >= argc) usage(); ts = ttstrtots(argv[i]); } else if(!strcmp(argv[i], "-rcc")){ opts |= RDBROCHKCON; } else { usage(); } } else if(!host){ host = argv[i]; } else if(!mhost){ mhost = argv[i]; } else { usage(); } } if(!host) usage(); int rv = procsetmst(host, port, mhost, mport, ts, opts); return rv; } /* parse arguments of repl command */ static int runrepl(int argc, char **argv){ char *host = NULL; int port = TTDEFPORT; uint64_t ts = 0; uint32_t sid = 0; bool ph = false; for(int i = 2; i < argc; i++){ if(!host && argv[i][0] == '-'){ if(!strcmp(argv[i], "-port")){ if(++i >= argc) usage(); port = tcatoi(argv[i]); } else if(!strcmp(argv[i], "-ts")){ if(++i >= argc) usage(); ts = ttstrtots(argv[i]); } else if(!strcmp(argv[i], "-sid")){ if(++i >= argc) usage(); sid = tcatoi(argv[i]); } else if(!strcmp(argv[i], "-ph")){ ph = true; } else { usage(); } } else if(!host){ host = argv[i]; } else { usage(); } } if(!host) usage(); int rv = procrepl(host, port, ts, sid, ph); return rv; } /* parse arguments of http command */ static int runhttp(int argc, char **argv){ char *url = NULL; struct { char *name; char *value; } heads[REQHEADMAX]; int hnum = 0; bool ih = false; for(int i = 2; i < argc; i++){ if(!url && argv[i][0] == '-'){ if(!strcmp(argv[i], "-ah")){ if(++i >= argc) usage(); char *name = argv[i]; if(++i >= argc) usage(); char *value = argv[i]; if(hnum < REQHEADMAX){ heads[hnum].name = name; heads[hnum].value = value; hnum++; } } else if(!strcmp(argv[i], "-ih")){ ih = true; } else { usage(); } } else if(!url){ url = argv[i]; } else { usage(); } } if(!url) usage(); TCMAP *hmap = tcmapnew2(hnum + 1); for(int i = 0; i < hnum; i++){ tcmapput2(hmap, heads[i].name, heads[i].value); } int rv = prochttp(url, hmap, ih); tcmapdel(hmap); return rv; } /* parse arguments of version command */ static int runversion(int argc, char **argv){ int rv = procversion(); return rv; } /* perform inform command */ static int procinform(const char *host, int port, bool st){ TCRDB *rdb = tcrdbnew(); if(!myopen(rdb, host, port)){ printerr(rdb); tcrdbdel(rdb); return 1; } bool err = false; if(st){ char *status = tcrdbstat(rdb); if(status){ printf("%s", status); tcfree(status); } else { printerr(rdb); err = true; } } else { printf("record number: %llu\n", (unsigned long long)tcrdbrnum(rdb)); printf("file size: %llu\n", (unsigned long long)tcrdbsize(rdb)); } if(!tcrdbclose(rdb)){ if(!err) printerr(rdb); err = true; } tcrdbdel(rdb); return err ? 1 : 0; } /* perform put command */ static int procput(const char *host, int port, const char *kbuf, int ksiz, const char *vbuf, int vsiz, int dmode, int shlw){ TCRDB *rdb = tcrdbnew(); if(!myopen(rdb, host, port)){ printerr(rdb); tcrdbdel(rdb); return 1; } bool err = false; int inum; double dnum; switch(dmode){ case -1: if(!tcrdbputkeep(rdb, kbuf, ksiz, vbuf, vsiz)){ printerr(rdb); err = true; } break; case 1: if(!tcrdbputcat(rdb, kbuf, ksiz, vbuf, vsiz)){ printerr(rdb); err = true; } break; case 10: inum = tcrdbaddint(rdb, kbuf, ksiz, tcatoi(vbuf)); if(inum == INT_MIN){ printerr(rdb); err = true; } else { printf("%d\n", inum); } break; case 11: dnum = tcrdbadddouble(rdb, kbuf, ksiz, tcatof(vbuf)); if(isnan(dnum)){ printerr(rdb); err = true; } else { printf("%.6f\n", dnum); } break; default: if(shlw >= 0){ if(!tcrdbputshl(rdb, kbuf, ksiz, vbuf, vsiz, shlw)){ printerr(rdb); err = true; } } else { if(!tcrdbput(rdb, kbuf, ksiz, vbuf, vsiz)){ printerr(rdb); err = true; } } break; } if(!tcrdbclose(rdb)){ if(!err) printerr(rdb); err = true; } tcrdbdel(rdb); return err ? 1 : 0; } /* perform out command */ static int procout(const char *host, int port, const char *kbuf, int ksiz){ TCRDB *rdb = tcrdbnew(); if(!myopen(rdb, host, port)){ printerr(rdb); tcrdbdel(rdb); return 1; } bool err = false; if(!tcrdbout(rdb, kbuf, ksiz)){ printerr(rdb); err = true; } if(!tcrdbclose(rdb)){ if(!err) printerr(rdb); err = true; } tcrdbdel(rdb); return err ? 1 : 0; } /* perform get command */ static int procget(const char *host, int port, const char *kbuf, int ksiz, int sep, bool px, bool pz){ TCRDB *rdb = tcrdbnew(); if(!myopen(rdb, host, port)){ printerr(rdb); tcrdbdel(rdb); return 1; } bool err = false; int vsiz; char *vbuf = tcrdbget(rdb, kbuf, ksiz, &vsiz); if(vbuf){ printdata(vbuf, vsiz, px, sep); if(!pz) putchar('\n'); tcfree(vbuf); } else { printerr(rdb); err = true; } if(!tcrdbclose(rdb)){ if(!err) printerr(rdb); err = true; } tcrdbdel(rdb); return err ? 1 : 0; } /* perform mget command */ static int procmget(const char *host, int port, const TCLIST *keys, int sep, bool px){ TCRDB *rdb = tcrdbnew(); if(!myopen(rdb, host, port)){ printerr(rdb); tcrdbdel(rdb); return 1; } bool err = false; TCMAP *recs = tcmapnew(); for(int i = 0; i < tclistnum(keys); i++){ int ksiz; const char *kbuf = tclistval(keys, i, &ksiz); tcmapput(recs, kbuf, ksiz, "", 0); } if(tcrdbget3(rdb, recs)){ tcmapiterinit(recs); const char *kbuf; int ksiz; while((kbuf = tcmapiternext(recs, &ksiz)) != NULL){ int vsiz; const char *vbuf = tcmapiterval(kbuf, &vsiz); printdata(kbuf, ksiz, px, sep); putchar('\t'); printdata(vbuf, vsiz, px, sep); putchar('\n'); } } else { printerr(rdb); err = true; } tcmapdel(recs); if(!tcrdbclose(rdb)){ if(!err) printerr(rdb); err = true; } tcrdbdel(rdb); return err ? 1 : 0; } /* perform list command */ static int proclist(const char *host, int port, int sep, int max, bool pv, bool px, const char *fmstr){ TCRDB *rdb = tcrdbnew(); if(!myopen(rdb, host, port)){ printerr(rdb); tcrdbdel(rdb); return 1; } bool err = false; if(fmstr){ TCLIST *keys = tcrdbfwmkeys2(rdb, fmstr, max); for(int i = 0; i < tclistnum(keys); i++){ int ksiz; const char *kbuf = tclistval(keys, i, &ksiz); printdata(kbuf, ksiz, px, sep); if(pv){ int vsiz; char *vbuf = tcrdbget(rdb, kbuf, ksiz, &vsiz); if(vbuf){ putchar('\t'); printdata(vbuf, vsiz, px, sep); tcfree(vbuf); } } putchar('\n'); } tclistdel(keys); } else { if(!tcrdbiterinit(rdb)){ printerr(rdb); err = true; } int ksiz; char *kbuf; int cnt = 0; while((kbuf = tcrdbiternext(rdb, &ksiz)) != NULL){ printdata(kbuf, ksiz, px, sep); if(pv){ int vsiz; char *vbuf = tcrdbget(rdb, kbuf, ksiz, &vsiz); if(vbuf){ putchar('\t'); printdata(vbuf, vsiz, px, sep); tcfree(vbuf); } } putchar('\n'); tcfree(kbuf); if(max >= 0 && ++cnt >= max) break; } } if(!tcrdbclose(rdb)){ if(!err) printerr(rdb); err = true; } tcrdbdel(rdb); return err ? 1 : 0; } /* perform ext command */ static int procext(const char *host, int port, const char *name, int opts, const char *kbuf, int ksiz, const char *vbuf, int vsiz, int sep, bool px, bool pz){ TCRDB *rdb = tcrdbnew(); if(!myopen(rdb, host, port)){ printerr(rdb); tcrdbdel(rdb); return 1; } bool err = false; int xsiz; char *xbuf = tcrdbext(rdb, name, opts, kbuf, ksiz, vbuf, vsiz, &xsiz); if(xbuf){ printdata(xbuf, xsiz, px, sep); if(!pz) putchar('\n'); tcfree(xbuf); } else { printerr(rdb); err = true; } if(!tcrdbclose(rdb)){ if(!err) printerr(rdb); err = true; } tcrdbdel(rdb); return err ? 1 : 0; } /* perform sync command */ static int procsync(const char *host, int port){ TCRDB *rdb = tcrdbnew(); if(!myopen(rdb, host, port)){ printerr(rdb); tcrdbdel(rdb); return 1; } bool err = false; if(!tcrdbsync(rdb)){ printerr(rdb); err = true; } if(!tcrdbclose(rdb)){ if(!err) printerr(rdb); err = true; } tcrdbdel(rdb); return err ? 1 : 0; } /* perform optimize command */ static int procoptimize(const char *host, int port, const char *param){ TCRDB *rdb = tcrdbnew(); if(!myopen(rdb, host, port)){ printerr(rdb); tcrdbdel(rdb); return 1; } bool err = false; if(!tcrdboptimize(rdb, param)){ printerr(rdb); err = true; } if(!tcrdbclose(rdb)){ if(!err) printerr(rdb); err = true; } tcrdbdel(rdb); return err ? 1 : 0; } /* perform vanish command */ static int procvanish(const char *host, int port){ TCRDB *rdb = tcrdbnew(); if(!myopen(rdb, host, port)){ printerr(rdb); tcrdbdel(rdb); return 1; } bool err = false; if(!tcrdbvanish(rdb)){ printerr(rdb); err = true; } if(!tcrdbclose(rdb)){ if(!err) printerr(rdb); err = true; } tcrdbdel(rdb); return err ? 1 : 0; } /* perform copy command */ static int proccopy(const char *host, int port, const char *dpath){ TCRDB *rdb = tcrdbnew(); if(!myopen(rdb, host, port)){ printerr(rdb); tcrdbdel(rdb); return 1; } bool err = false; if(!tcrdbcopy(rdb, dpath)){ printerr(rdb); err = true; } if(!tcrdbclose(rdb)){ if(!err) printerr(rdb); err = true; } tcrdbdel(rdb); return err ? 1 : 0; } /* perform misc command */ static int procmisc(const char *host, int port, const char *func, int opts, const TCLIST *args, int sep, bool px){ TCRDB *rdb = tcrdbnew(); if(!myopen(rdb, host, port)){ printerr(rdb); tcrdbdel(rdb); return 1; } bool err = false; TCLIST *res = tcrdbmisc(rdb, func, opts, args); if(res){ for(int i = 0; i < tclistnum(res); i++){ int rsiz; const char *rbuf = tclistval(res, i, &rsiz); printdata(rbuf, rsiz, px, sep); printf("\n"); } tclistdel(res); } else { printerr(rdb); err = true; } if(!tcrdbclose(rdb)){ if(!err) printerr(rdb); err = true; } tcrdbdel(rdb); return err ? 1 : 0; } /* perform importtsv command */ static int procimporttsv(const char *host, int port, const char *file, bool nr, bool sc, int sep){ FILE *ifp = file ? fopen(file, "rb") : stdin; if(!ifp){ fprintf(stderr, "%s: could not open\n", file ? file : "(stdin)"); return 1; } TCRDB *rdb = tcrdbnew(); if(!myopen(rdb, host, port)){ printerr(rdb); tcrdbdel(rdb); if(ifp != stdin) fclose(ifp); return 1; } bool err = false; char *line; int cnt = 0; while(!err && (line = mygetline(ifp)) != NULL){ char *pv = strchr(line, '\t'); if(!pv){ tcfree(line); continue; } *pv = '\0'; if(sc) tcstrtolower(line); char *vbuf; int vsiz; if(sep > 0){ vbuf = strtozsv(pv + 1, sep, &vsiz); } else { vsiz = strlen(pv + 1); vbuf = tcmemdup(pv + 1, vsiz); } if(nr){ if(!tcrdbputnr(rdb, line, pv - line, vbuf, vsiz)){ printerr(rdb); err = true; } } else { if(!tcrdbput(rdb, line, pv - line, vbuf, vsiz)){ printerr(rdb); err = true; } } tcfree(vbuf); tcfree(line); if(cnt > 0 && cnt % 100 == 0){ putchar('.'); fflush(stdout); if(cnt % 5000 == 0) printf(" (%08d)\n", cnt); } cnt++; } printf(" (%08d)\n", cnt); if(!tcrdbclose(rdb)){ if(!err) printerr(rdb); err = true; } tcrdbdel(rdb); if(ifp != stdin) fclose(ifp); return err ? 1 : 0; } /* perform restore command */ static int procrestore(const char *host, int port, const char *upath, uint64_t ts, int opts){ TCRDB *rdb = tcrdbnew(); if(!myopen(rdb, host, port)){ printerr(rdb); tcrdbdel(rdb); return 1; } bool err = false; if(!tcrdbrestore(rdb, upath, ts, opts)){ printerr(rdb); err = true; } if(!tcrdbclose(rdb)){ if(!err) printerr(rdb); err = true; } tcrdbdel(rdb); return err ? 1 : 0; } /* perform setmst command */ static int procsetmst(const char *host, int port, const char *mhost, int mport, uint64_t ts, int opts){ TCRDB *rdb = tcrdbnew(); if(!myopen(rdb, host, port)){ printerr(rdb); tcrdbdel(rdb); return 1; } bool err = false; if(!mysetmst(rdb, mhost, mport, ts, opts)){ printerr(rdb); err = true; } if(!tcrdbclose(rdb)){ if(!err) printerr(rdb); err = true; } tcrdbdel(rdb); return err ? 1 : 0; } /* perform repl command */ static int procrepl(const char *host, int port, uint64_t ts, uint32_t sid, bool ph){ bool err = false; TCREPL *repl = tcreplnew(); if(tcreplopen(repl, host, port, ts, sid)){ const char *rbuf; int rsiz; uint64_t rts; uint32_t rsid; char stack[TTIOBUFSIZ]; while((rbuf = tcreplread(repl, &rsiz, &rts, &rsid)) != NULL){ if(rsiz < 1) continue; if(ph){ printf("%llu\t%u:%u\t", (unsigned long long)rts, (unsigned int)rsid, (unsigned int)repl->mid); if(rsiz >= 2){ printf("%s\t", ttcmdidtostr(((unsigned char *)rbuf)[1])); printdata(rbuf, rsiz, true, -1); putchar('\n'); } else { printf("[broken entry]\n"); } fflush(stdout); } else { int msiz = sizeof(uint8_t) + sizeof(uint64_t) + sizeof(uint32_t) * 2 + rsiz; char *mbuf = (msiz < TTIOBUFSIZ) ? stack : tcmalloc(msiz); unsigned char *wp = (unsigned char *)mbuf; *(wp++) = TCULMAGICNUM; uint64_t llnum = TTHTONLL(rts); memcpy(wp, &llnum, sizeof(llnum)); wp += sizeof(llnum); uint16_t snum = TTHTONS(rsid); memcpy(wp, &snum, sizeof(snum)); wp += sizeof(snum); snum = TTHTONS(repl->mid); memcpy(wp, &snum, sizeof(snum)); wp += sizeof(snum); uint32_t lnum = TTHTONL(rsiz); memcpy(wp, &lnum, sizeof(lnum)); wp += sizeof(lnum); memcpy(wp, rbuf, rsiz); fwrite(mbuf, 1, msiz, stdout); fflush(stdout); if(mbuf != stack) tcfree(mbuf); } } tcreplclose(repl); } else { fprintf(stderr, "%s: %s:%d could not be connected\n", g_progname, host, port); err = true; } tcrepldel(repl); return err ? 1 : 0; } /* perform http command */ static int prochttp(const char *url, TCMAP *hmap, bool ih){ bool err = false; TCMAP *resheads = ih ? tcmapnew2(MINIBNUM) : NULL; TCXSTR *body = tcxstrnew(); int code = tthttpfetch(url, hmap, resheads, body); if(code > 0){ if(resheads){ printf("%s\n", tcmapget2(resheads, "STATUS")); tcmapiterinit(resheads); const char *name; while((name = tcmapiternext2(resheads)) != NULL){ if(*name >= 'A' && *name <= 'Z') continue; char *cap = tcstrdup(name); tcstrtolower(cap); char *wp = cap; bool head = true; while(*wp != '\0'){ if(head && *wp >= 'a' && *wp <= 'z') *wp -= 'a' - 'A'; head = *wp == '-' || *wp == ' '; wp++; } printf("%s: %s\n", cap, tcmapiterval2(name)); tcfree(cap); } printf("\n"); } fwrite(tcxstrptr(body), 1, tcxstrsize(body), stdout); } else { fprintf(stderr, "%s: %s: could not be connected\n", g_progname, url); err = true; } tcxstrdel(body); if(resheads) tcmapdel(resheads); return err ? 1 : 0; } /* perform version command */ static int procversion(void){ printf("Tokyo Tyrant version %s (%d:%s) for %s\n", ttversion, _TT_LIBVER, _TT_PROTVER, TTSYSNAME); printf("Copyright (C) 2007-2010 Mikio Hirabayashi\n"); return 0; } // END OF FILE tokyotyrant-1.1.40/tcrmttest.c000066400000000000000000000623161133351147200164110ustar00rootroot00000000000000/************************************************************************************************* * The test cases of the remote database API * Copyright (C) 2006-2010 Mikio Hirabayashi * This file is part of Tokyo Tyrant. * Tokyo Tyrant is free software; you can redistribute it and/or modify it under the terms of * the GNU Lesser General Public License as published by the Free Software Foundation; either * version 2.1 of the License or any later version. Tokyo Tyrant 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 Lesser General Public * License for more details. * You should have received a copy of the GNU Lesser General Public License along with Tokyo * Tyrant; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA. *************************************************************************************************/ #include #include "myconf.h" #define RECBUFSIZ 32 // buffer for records typedef struct { // type of structure for write thread TCRDB *rdb; int rnum; bool nr; const char *ext; bool rnd; int id; } TARGWRITE; typedef struct { // type of structure for read thread TCRDB *rdb; int rnum; int mul; bool rnd; int id; } TARGREAD; typedef struct { // type of structure for remove thread TCRDB *rdb; int rnum; bool rnd; int id; } TARGREMOVE; typedef struct { // type of structure for typical thread TCRDB *rdb; int rnum; int id; } TARGTYPICAL; typedef struct { // type of structure for table thread TCRDB *rdb; int rnum; bool rnd; int id; } TARGTABLE; /* global variables */ const char *g_progname; // program name /* function prototypes */ int main(int argc, char **argv); static void usage(void); static void iprintf(const char *format, ...); static void eprint(TCRDB *rdb, int line, const char *func); static int myrand(int range); static int myrandnd(int range); static bool myopen(TCRDB *rdb, const char *host, int port); static int runwrite(int argc, char **argv); static int runread(int argc, char **argv); static int runremove(int argc, char **argv); static int runtypical(int argc, char **argv); static int runtable(int argc, char **argv); static int procwrite(const char *host, int port, int tnum, int rnum, bool nr, const char *ext, bool rnd); static int procread(const char *host, int port, int tnum, int mul, bool rnd); static int procremove(const char *host, int port, int tnum, bool rnd); static int proctypical(const char *host, int port, int tnum, int rnum); static int proctable(const char *host, int port, int tnum, int rnum, bool rnd); static void *threadwrite(void *targ); static void *threadread(void *targ); static void *threadremove(void *targ); static void *threadtypical(void *targ); static void *threadtable(void *targ); /* main routine */ int main(int argc, char **argv){ g_progname = argv[0]; srand((unsigned int)(tctime() * 1000) % UINT_MAX); if(argc < 2) usage(); int rv = 0; if(!strcmp(argv[1], "write")){ rv = runwrite(argc, argv); } else if(!strcmp(argv[1], "read")){ rv = runread(argc, argv); } else if(!strcmp(argv[1], "remove")){ rv = runremove(argc, argv); } else if(!strcmp(argv[1], "typical")){ rv = runtypical(argc, argv); } else if(!strcmp(argv[1], "table")){ rv = runtable(argc, argv); } else { usage(); } return rv; } /* print the usage and exit */ static void usage(void){ fprintf(stderr, "%s: test cases of the remote database API of Tokyo Tyrant\n", g_progname); fprintf(stderr, "\n"); fprintf(stderr, "usage:\n"); fprintf(stderr, " %s write [-port num] [-tnum num] [-nr] [-ext name] [-rnd] host rnum\n", g_progname); fprintf(stderr, " %s read [-port num] [-tnum num] [-mul num] host\n", g_progname); fprintf(stderr, " %s remove [-port num] [-tnum num] host\n", g_progname); fprintf(stderr, " %s typical [-port num] [-tnum num] host rnum\n", g_progname); fprintf(stderr, " %s table [-port num] [-tnum num] host rnum\n", g_progname); fprintf(stderr, "\n"); exit(1); } /* print formatted information string and flush the buffer */ static void iprintf(const char *format, ...){ va_list ap; va_start(ap, format); vprintf(format, ap); fflush(stdout); va_end(ap); } /* print error message of abstract database */ static void eprint(TCRDB *rdb, int line, const char *func){ int ecode = tcrdbecode(rdb); fprintf(stderr, "%s: %d: %s: error: %d: %s\n", g_progname, line, func, ecode, tcrdberrmsg(ecode)); } /* get a random number */ static int myrand(int range){ if(range < 2) return 0; int high = (unsigned int)rand() >> 4; int low = range * (rand() / (RAND_MAX + 1.0)); low &= (unsigned int)INT_MAX >> 4; return (high + low) % range; } /* get a random number based on normal distribution */ static int myrandnd(int range){ int num = (int)tcdrandnd(range >> 1, range / 10); return (num < 0 || num >= range) ? 0 : num; } /* open the remote database */ static bool myopen(TCRDB *rdb, const char *host, int port){ bool err = false; if(strchr(host, ':') || strchr(host, '#')){ if(!tcrdbopen2(rdb, host)) err = true; } else { if(!tcrdbopen(rdb, host, port)) err = true; } return !err; } /* parse arguments of write command */ static int runwrite(int argc, char **argv){ char *host = NULL; char *rstr = NULL; int port = TTDEFPORT; int tnum = 1; bool nr = false; char *ext = NULL; bool rnd = false; for(int i = 2; i < argc; i++){ if(!host && argv[i][0] == '-'){ if(!strcmp(argv[i], "-port")){ if(++i >= argc) usage(); port = tcatoi(argv[i]); } else if(!strcmp(argv[i], "-tnum")){ if(++i >= argc) usage(); tnum = tcatoi(argv[i]); } else if(!strcmp(argv[i], "-nr")){ nr = true; } else if(!strcmp(argv[i], "-ext")){ if(++i >= argc) usage(); ext = argv[i]; } else if(!strcmp(argv[i], "-rnd")){ rnd = true; } else { usage(); } } else if(!host){ host = argv[i]; } else if(!rstr){ rstr = argv[i]; } else { usage(); } } if(!host || !rstr || tnum < 1) usage(); int rnum = tcatoi(rstr); if(rnum < 1) usage(); int rv = procwrite(host, port, tnum, rnum, nr, ext, rnd); return rv; } /* parse arguments of read command */ static int runread(int argc, char **argv){ char *host = NULL; int port = TTDEFPORT; int tnum = 1; int mul = 0; bool rnd = false; for(int i = 2; i < argc; i++){ if(!host && argv[i][0] == '-'){ if(!strcmp(argv[i], "-port")){ if(++i >= argc) usage(); port = tcatoi(argv[i]); } else if(!strcmp(argv[i], "-tnum")){ if(++i >= argc) usage(); tnum = tcatoi(argv[i]); } else if(!strcmp(argv[i], "-mul")){ if(++i >= argc) usage(); mul = tcatoi(argv[i]); } else if(!strcmp(argv[i], "-rnd")){ rnd = true; } else { usage(); } } else if(!host){ host = argv[i]; } else { usage(); } } if(!host || tnum < 1) usage(); int rv = procread(host, port, tnum, mul, rnd); return rv; } /* parse arguments of remove command */ static int runremove(int argc, char **argv){ char *host = NULL; int port = TTDEFPORT; int tnum = 1; bool rnd = false; for(int i = 2; i < argc; i++){ if(!host && argv[i][0] == '-'){ if(!strcmp(argv[i], "-port")){ if(++i >= argc) usage(); port = tcatoi(argv[i]); } else if(!strcmp(argv[i], "-tnum")){ if(++i >= argc) usage(); tnum = tcatoi(argv[i]); } else if(!strcmp(argv[i], "-rnd")){ rnd = true; } else { usage(); } } else if(!host){ host = argv[i]; } else { usage(); } } if(!host || tnum < 1) usage(); int rv = procremove(host, port, tnum, rnd); return rv; } /* parse arguments of typical command */ static int runtypical(int argc, char **argv){ char *host = NULL; char *rstr = NULL; int port = TTDEFPORT; int tnum = 1; for(int i = 2; i < argc; i++){ if(!host && argv[i][0] == '-'){ if(!strcmp(argv[i], "-port")){ if(++i >= argc) usage(); port = tcatoi(argv[i]); } else if(!strcmp(argv[i], "-tnum")){ if(++i >= argc) usage(); tnum = tcatoi(argv[i]); } else { usage(); } } else if(!host){ host = argv[i]; } else if(!rstr){ rstr = argv[i]; } else { usage(); } } if(!host || !rstr || tnum < 1) usage(); int rnum = tcatoi(rstr); if(rnum < 1) usage(); int rv = proctypical(host, port, tnum, rnum); return rv; } /* parse arguments of table command */ static int runtable(int argc, char **argv){ char *host = NULL; char *rstr = NULL; int port = TTDEFPORT; int tnum = 1; bool rnd = false; for(int i = 2; i < argc; i++){ if(!host && argv[i][0] == '-'){ if(!strcmp(argv[i], "-port")){ if(++i >= argc) usage(); port = tcatoi(argv[i]); } else if(!strcmp(argv[i], "-tnum")){ if(++i >= argc) usage(); tnum = tcatoi(argv[i]); } else if(!strcmp(argv[i], "-rnd")){ rnd = true; } else { usage(); } } else if(!host){ host = argv[i]; } else if(!rstr){ rstr = argv[i]; } else { usage(); } } if(!host || !rstr || tnum < 1) usage(); int rnum = tcatoi(rstr); if(rnum < 1) usage(); int rv = proctable(host, port, tnum, rnum, rnd); return rv; } /* perform write command */ static int procwrite(const char *host, int port, int tnum, int rnum, bool nr, const char *ext, bool rnd){ iprintf("\n host=%s port=%d tnum=%d rnum=%d nr=%d ext=%s rnd=%d\n\n", host, port, tnum, rnum, nr, ext ? ext : "", rnd); bool err = false; double stime = tctime(); TCRDB *rdbs[tnum]; for(int i = 0; i < tnum; i++){ rdbs[i] = tcrdbnew(); if(!myopen(rdbs[i], host, port)){ eprint(rdbs[i], __LINE__, "tcrdbopen"); err = true; } } TCRDB *rdb = rdbs[0]; TARGWRITE targs[tnum]; pthread_t threads[tnum]; if(tnum == 1){ targs[0].rdb = rdbs[0]; targs[0].rnum = rnum; targs[0].nr = nr; targs[0].ext = ext; targs[0].rnd = rnd; targs[0].id = 0; if(threadwrite(targs) != NULL) err = true; } else { for(int i = 0; i < tnum; i++){ targs[i].rdb = rdbs[i]; targs[i].rnum = rnum; targs[i].nr = nr; targs[i].ext = ext; targs[i].rnd = rnd; targs[i].id = i; if(pthread_create(threads + i, NULL, threadwrite, targs + i) != 0){ eprint(rdb, __LINE__, "pthread_create"); targs[i].id = -1; err = true; } } for(int i = 0; i < tnum; i++){ if(targs[i].id == -1) continue; void *rv; if(pthread_join(threads[i], &rv) != 0){ eprint(rdb, __LINE__, "pthread_join"); err = true; } else if(rv){ err = true; } } } iprintf("record number: %llu\n", (unsigned long long)tcrdbrnum(rdb)); iprintf("size: %llu\n", (unsigned long long)tcrdbsize(rdb)); for(int i = 0; i < tnum; i++){ if(!tcrdbclose(rdbs[i])){ eprint(rdbs[i], __LINE__, "tcrdbclose"); err = true; } tcrdbdel(rdbs[i]); } iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } /* perform read command */ static int procread(const char *host, int port, int tnum, int mul, bool rnd){ iprintf("\n host=%s port=%d tnum=%d mul=%d rnd=%d\n\n", host, port, tnum, mul, rnd); bool err = false; double stime = tctime(); TCRDB *rdbs[tnum]; for(int i = 0; i < tnum; i++){ rdbs[i] = tcrdbnew(); if(!myopen(rdbs[i], host, port)){ eprint(rdbs[i], __LINE__, "tcrdbopen"); err = true; } } TCRDB *rdb = rdbs[0]; int rnum = tcrdbrnum(rdb) / tnum; TARGREAD targs[tnum]; pthread_t threads[tnum]; if(tnum == 1){ targs[0].rdb = rdbs[0]; targs[0].rnum = rnum; targs[0].mul = mul; targs[0].rnd = rnd; targs[0].id = 0; if(threadread(targs) != NULL) err = true; } else { for(int i = 0; i < tnum; i++){ targs[i].rdb = rdbs[i]; targs[i].rnum = rnum; targs[i].mul = mul; targs[i].rnd = rnd; targs[i].id = i; if(pthread_create(threads + i, NULL, threadread, targs + i) != 0){ eprint(rdb, __LINE__, "pthread_create"); targs[i].id = -1; err = true; } } for(int i = 0; i < tnum; i++){ if(targs[i].id == -1) continue; void *rv; if(pthread_join(threads[i], &rv) != 0){ eprint(rdb, __LINE__, "pthread_join"); err = true; } else if(rv){ err = true; } } } iprintf("record number: %llu\n", (unsigned long long)tcrdbrnum(rdb)); iprintf("size: %llu\n", (unsigned long long)tcrdbsize(rdb)); for(int i = 0; i < tnum; i++){ if(!tcrdbclose(rdbs[i])){ eprint(rdbs[i], __LINE__, "tcrdbclose"); err = true; } tcrdbdel(rdbs[i]); } iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } /* perform remove command */ static int procremove(const char *host, int port, int tnum, bool rnd){ iprintf("\n host=%s port=%d tnum=%d rnd=%d\n\n", host, port, tnum, rnd); bool err = false; double stime = tctime(); TCRDB *rdbs[tnum]; for(int i = 0; i < tnum; i++){ rdbs[i] = tcrdbnew(); if(!myopen(rdbs[i], host, port)){ eprint(rdbs[i], __LINE__, "tcrdbopen"); err = true; } } TCRDB *rdb = rdbs[0]; int rnum = tcrdbrnum(rdb) / tnum; TARGREMOVE targs[tnum]; pthread_t threads[tnum]; if(tnum == 1){ targs[0].rdb = rdbs[0]; targs[0].rnum = rnum; targs[0].rnd = rnd; targs[0].id = 0; if(threadremove(targs) != NULL) err = true; } else { for(int i = 0; i < tnum; i++){ targs[i].rdb = rdbs[i]; targs[i].rnum = rnum; targs[i].rnd = rnd; targs[i].id = i; if(pthread_create(threads + i, NULL, threadremove, targs + i) != 0){ eprint(rdb, __LINE__, "pthread_create"); targs[i].id = -1; err = true; } } for(int i = 0; i < tnum; i++){ if(targs[i].id == -1) continue; void *rv; if(pthread_join(threads[i], &rv) != 0){ eprint(rdb, __LINE__, "pthread_join"); err = true; } else if(rv){ err = true; } } } iprintf("record number: %llu\n", (unsigned long long)tcrdbrnum(rdb)); iprintf("size: %llu\n", (unsigned long long)tcrdbsize(rdb)); for(int i = 0; i < tnum; i++){ if(!tcrdbclose(rdbs[i])){ eprint(rdbs[i], __LINE__, "tcrdbclose"); err = true; } tcrdbdel(rdbs[i]); } iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } /* perform typical command */ static int proctypical(const char *host, int port, int tnum, int rnum){ iprintf("\n host=%s port=%d tnum=%d rnum=%d\n\n", host, port, tnum, rnum); bool err = false; double stime = tctime(); TCRDB *rdbs[tnum]; for(int i = 0; i < tnum; i++){ rdbs[i] = tcrdbnew(); if(!myopen(rdbs[i], host, port)){ eprint(rdbs[i], __LINE__, "tcrdbopen"); err = true; } } TCRDB *rdb = rdbs[0]; TARGTYPICAL targs[tnum]; pthread_t threads[tnum]; if(tnum == 1){ targs[0].rdb = rdbs[0]; targs[0].rnum = rnum; targs[0].id = 0; if(threadtypical(targs) != NULL) err = true; } else { for(int i = 0; i < tnum; i++){ targs[i].rdb = rdbs[i]; targs[i].rnum = rnum; targs[i].id = i; if(pthread_create(threads + i, NULL, threadtypical, targs + i) != 0){ eprint(rdb, __LINE__, "pthread_create"); targs[i].id = -1; err = true; } } for(int i = 0; i < tnum; i++){ if(targs[i].id == -1) continue; void *rv; if(pthread_join(threads[i], &rv) != 0){ eprint(rdb, __LINE__, "pthread_join"); err = true; } else if(rv){ err = true; } } } iprintf("record number: %llu\n", (unsigned long long)tcrdbrnum(rdb)); iprintf("size: %llu\n", (unsigned long long)tcrdbsize(rdb)); for(int i = 0; i < tnum; i++){ if(!tcrdbclose(rdbs[i])){ eprint(rdbs[i], __LINE__, "tcrdbclose"); err = true; } tcrdbdel(rdbs[i]); } iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } /* perform table command */ static int proctable(const char *host, int port, int tnum, int rnum, bool rnd){ iprintf("\n host=%s port=%d tnum=%d rnum=%d rnd=%d\n\n", host, port, tnum, rnum, rnd); bool err = false; double stime = tctime(); TCRDB *rdbs[tnum]; for(int i = 0; i < tnum; i++){ rdbs[i] = tcrdbnew(); if(!myopen(rdbs[i], host, port)){ eprint(rdbs[i], __LINE__, "tcrdbopen"); err = true; } } TCRDB *rdb = rdbs[0]; if(!tcrdbvanish(rdb)){ eprint(rdb, __LINE__, "tcrdbvanish"); err = true; } if(!tcrdbtblsetindex(rdb, "c", RDBITLEXICAL)){ eprint(rdb, __LINE__, "tcrdbtblsetindex"); err = true; } if(!tcrdbtblsetindex(rdb, "x", RDBITDECIMAL)){ eprint(rdb, __LINE__, "tcrdbtblsetindex"); err = true; } TARGTABLE targs[tnum]; pthread_t threads[tnum]; if(tnum == 1){ targs[0].rdb = rdbs[0]; targs[0].rnum = rnum; targs[0].rnd = rnd; targs[0].id = 0; if(threadtable(targs) != NULL) err = true; } else { for(int i = 0; i < tnum; i++){ targs[i].rdb = rdbs[i]; targs[i].rnum = rnum; targs[i].rnd = rnd; targs[i].id = i; if(pthread_create(threads + i, NULL, threadtable, targs + i) != 0){ eprint(rdb, __LINE__, "pthread_create"); targs[i].id = -1; err = true; } } for(int i = 0; i < tnum; i++){ if(targs[i].id == -1) continue; void *rv; if(pthread_join(threads[i], &rv) != 0){ eprint(rdb, __LINE__, "pthread_join"); err = true; } else if(rv){ err = true; } } } iprintf("record number: %llu\n", (unsigned long long)tcrdbrnum(rdb)); iprintf("size: %llu\n", (unsigned long long)tcrdbsize(rdb)); for(int i = 0; i < tnum; i++){ if(!tcrdbclose(rdbs[i])){ eprint(rdbs[i], __LINE__, "tcrdbclose"); err = true; } tcrdbdel(rdbs[i]); } iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } /* thread the write function */ static void *threadwrite(void *targ){ TCRDB *rdb = ((TARGWRITE *)targ)->rdb; int rnum = ((TARGWRITE *)targ)->rnum; bool nr = ((TARGWRITE *)targ)->nr; const char *ext = ((TARGWRITE *)targ)->ext; bool rnd = ((TARGWRITE *)targ)->rnd; int id = ((TARGWRITE *)targ)->id; bool err = false; int base = id * rnum; for(int i = 1; i <= rnum; i++){ char buf[RECBUFSIZ]; int len = sprintf(buf, "%08d", base + (rnd ? myrand(i) + 1 : i)); if(nr){ if(!tcrdbputnr(rdb, buf, len, buf, len)){ eprint(rdb, __LINE__, "tcrdbputnr"); err = true; break; } } else if(ext){ int rsiz; char *rbuf = tcrdbext(rdb, ext, 0, buf, len, buf, len, &rsiz); if(!rbuf && tcrdbecode(rdb) != TCEMISC){ eprint(rdb, __LINE__, "tcrdbext"); err = true; break; } tcfree(rbuf); } else { if(!tcrdbput(rdb, buf, len, buf, len)){ eprint(rdb, __LINE__, "tcrdbput"); err = true; break; } } if(id == 0 && rnum > 250 && i % (rnum / 250) == 0){ putchar('.'); fflush(stdout); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } return err ? "error" : NULL; } /* thread the read function */ static void *threadread(void *targ){ TCRDB *rdb = ((TARGREAD *)targ)->rdb; int rnum = ((TARGREAD *)targ)->rnum; int mul = ((TARGREAD *)targ)->mul; bool rnd = ((TARGREAD *)targ)->rnd; int id = ((TARGREAD *)targ)->id; bool err = false; int base = id * rnum; TCMAP *recs = mul > 1 ? tcmapnew() : NULL; for(int i = 1; i <= rnum && !err; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%08d", base + (rnd ? myrandnd(i) + 1 : i)); if(mul > 1){ tcmapput(recs, kbuf, ksiz, kbuf, ksiz); if(i % mul == 0){ if(!tcrdbget3(rdb, recs)){ eprint(rdb, __LINE__, "tcrdbget3"); err = true; break; } tcmapclear(recs); } } else { int vsiz; char *vbuf = tcrdbget(rdb, kbuf, ksiz, &vsiz); if(!vbuf && !rnd){ eprint(rdb, __LINE__, "tcrdbget"); err = true; break; } tcfree(vbuf); } if(id == 0 && rnum > 250 && i % (rnum / 250) == 0){ putchar('.'); fflush(stdout); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } if(recs) tcmapdel(recs); return err ? "error" : NULL; } /* thread the remove function */ static void *threadremove(void *targ){ TCRDB *rdb = ((TARGREMOVE *)targ)->rdb; int rnum = ((TARGREMOVE *)targ)->rnum; bool rnd = ((TARGREMOVE *)targ)->rnd; int id = ((TARGREMOVE *)targ)->id; bool err = false; int base = id * rnum; for(int i = 1; i <= rnum && !err; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%08d", base + (rnd ? myrandnd(i) + 1 : i)); if(!tcrdbout(rdb, kbuf, ksiz) && !rnd){ eprint(rdb, __LINE__, "tcrdbout"); err = true; break; } if(id == 0 && rnum > 250 && i % (rnum / 250) == 0){ putchar('.'); fflush(stdout); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } return err ? "error" : NULL; } /* thread the typical function */ static void *threadtypical(void *targ){ TCRDB *rdb = ((TARGTYPICAL *)targ)->rdb; int rnum = ((TARGTYPICAL *)targ)->rnum; int id = ((TARGTYPICAL *)targ)->id; bool err = false; int range = (id + 1) * rnum; for(int i = 1; i <= rnum && !err; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%d", myrand(range) + 1); char *vbuf; int vsiz; switch(myrand(6)){ case 0: if(!tcrdbput(rdb, kbuf, ksiz, kbuf, ksiz)){ eprint(rdb, __LINE__, "tcrdbput"); err = true; } break; case 1: if(!tcrdbputkeep(rdb, kbuf, ksiz, kbuf, ksiz) && tcrdbecode(rdb) != TTEKEEP){ eprint(rdb, __LINE__, "tcrdbputkeep"); err = true; } break; case 2: if(!tcrdbputcat(rdb, kbuf, ksiz, kbuf, ksiz)){ eprint(rdb, __LINE__, "tcrdbputcat"); err = true; } break; case 3: if(!tcrdbputnr(rdb, kbuf, ksiz, kbuf, ksiz)){ eprint(rdb, __LINE__, "tcrdbputnr"); err = true; } break; case 4: if(!tcrdbout(rdb, kbuf, ksiz) && tcrdbecode(rdb) != TTENOREC){ eprint(rdb, __LINE__, "tcrdbout"); err = true; } break; default: vbuf = tcrdbget(rdb, kbuf, ksiz, &vsiz); if(vbuf){ tcfree(vbuf); } else if(tcrdbecode(rdb) != TTENOREC){ eprint(rdb, __LINE__, "tcrdbget"); err = true; } } if(id == 0 && rnum > 250 && i % (rnum / 250) == 0){ putchar('.'); fflush(stdout); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } return err ? "error" : NULL; } /* thread the table function */ static void *threadtable(void *targ){ TCRDB *rdb = ((TARGTABLE *)targ)->rdb; int rnum = ((TARGTABLE *)targ)->rnum; bool rnd = ((TARGREMOVE *)targ)->rnd; int id = ((TARGTABLE *)targ)->id; bool err = false; int base = id * rnum; for(int i = 1; i <= rnum && !err; i++){ char pkbuf[RECBUFSIZ]; int pksiz = sprintf(pkbuf, "%08d", base + (rnd ? myrand(rnum / 2 + i) : i)); TCMAP *cols = tcmapnew2(7); char vbuf[RECBUFSIZ*5]; int vsiz = sprintf(vbuf, "%d", myrand(i) + 1); tcmapput(cols, "c", 1, vbuf, vsiz); vsiz = sprintf(vbuf, "%lld", (long long)tctime() + (rnd ? myrand(3600) + 1 : 60)); tcmapput(cols, "x", 1, vbuf, vsiz); if(rnd){ int act = myrand(100); if(act < 5){ RDBQRY *qry = tcrdbqrynew(rdb); if(myrand(5) == 0){ sprintf(vbuf, "%d", myrand(i)); tcrdbqryaddcond(qry, "c", RDBQCSTREQ, vbuf); } else { sprintf(vbuf, "%d,%d", myrand(rnum), myrand(rnum)); tcrdbqryaddcond(qry, "x", RDBQCNUMBT, vbuf); } tcrdbqrysetlimit(qry, 10, 0); TCLIST *res = tcrdbqrysearch(qry); tclistdel(res); tcrdbqrydel(qry); } else if(act < 10){ if(!tcrdbtblout(rdb, pkbuf, pksiz) && tcrdbecode(rdb) != TTENOREC){ eprint(rdb, __LINE__, "tcrdbtblout"); err = true; } } else { if(!tcrdbtblput(rdb, pkbuf, pksiz, cols)){ eprint(rdb, __LINE__, "tcrdbtblput"); err = true; } } } else { if(!tcrdbtblput(rdb, pkbuf, pksiz, cols)){ eprint(rdb, __LINE__, "tcrdbtblput"); err = true; } } tcmapdel(cols); if(id == 0 && rnum > 250 && i % (rnum / 250) == 0){ putchar('.'); fflush(stdout); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } return err ? "error" : NULL; } // END OF FILE tokyotyrant-1.1.40/tcrtest.c000066400000000000000000001320321133351147200160410ustar00rootroot00000000000000/************************************************************************************************* * The test cases of the remote database API * Copyright (C) 2006-2010 Mikio Hirabayashi * This file is part of Tokyo Tyrant. * Tokyo Tyrant is free software; you can redistribute it and/or modify it under the terms of * the GNU Lesser General Public License as published by the Free Software Foundation; either * version 2.1 of the License or any later version. Tokyo Tyrant 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 Lesser General Public * License for more details. * You should have received a copy of the GNU Lesser General Public License along with Tokyo * Tyrant; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA. *************************************************************************************************/ #include #include "myconf.h" #define RECBUFSIZ 32 // buffer for records /* global variables */ const char *g_progname; // program name /* function prototypes */ int main(int argc, char **argv); static void usage(void); static void iprintf(const char *format, ...); static void iputchar(int c); static void eprint(TCRDB *rdb, int line, const char *func); static int myrand(int range); static bool myopen(TCRDB *rdb, const char *host, int port); static int runwrite(int argc, char **argv); static int runread(int argc, char **argv); static int runremove(int argc, char **argv); static int runrcat(int argc, char **argv); static int runmisc(int argc, char **argv); static int runwicked(int argc, char **argv); static int runtable(int argc, char **argv); static int procwrite(const char *host, int port, int cnum, int tout, int rnum, bool nr, bool rnd); static int procread(const char *host, int port, int cnum, int tout, int mul, bool rnd); static int procremove(const char *host, int port, int cnum, int tout, bool rnd); static int procrcat(const char *host, int port, int cnum, int tout, int rnum, int shl, bool dai, bool dad, const char *ext, int xopts); static int procmisc(const char *host, int port, int cnum, int tout, int rnum); static int procwicked(const char *host, int port, int cnum, int tout, int rnum); static int proctable(const char *host, int port, int cnum, int tout, int rnum, int exp); /* main routine */ int main(int argc, char **argv){ g_progname = argv[0]; srand((unsigned int)(tctime() * 1000) % UINT_MAX); signal(SIGPIPE, SIG_IGN); if(argc < 2) usage(); int rv = 0; if(!strcmp(argv[1], "write")){ rv = runwrite(argc, argv); } else if(!strcmp(argv[1], "read")){ rv = runread(argc, argv); } else if(!strcmp(argv[1], "remove")){ rv = runremove(argc, argv); } else if(!strcmp(argv[1], "rcat")){ rv = runrcat(argc, argv); } else if(!strcmp(argv[1], "misc")){ rv = runmisc(argc, argv); } else if(!strcmp(argv[1], "wicked")){ rv = runwicked(argc, argv); } else if(!strcmp(argv[1], "table")){ rv = runtable(argc, argv); } else { usage(); } return rv; } /* print the usage and exit */ static void usage(void){ fprintf(stderr, "%s: test cases of the remote database API of Tokyo Tyrant\n", g_progname); fprintf(stderr, "\n"); fprintf(stderr, "usage:\n"); fprintf(stderr, " %s write [-port num] [-cnum num] [-tout num] [-nr] [-rnd] host rnum\n", g_progname); fprintf(stderr, " %s read [-port num] [-cnum num] [-tout num] [-mul num] [-rnd] host\n", g_progname); fprintf(stderr, " %s remove [-port num] [-cnum num] [-tout num] [-rnd] host\n", g_progname); fprintf(stderr, " %s rcat [-port num] [-cnum num] [-tout num] [-shl num] [-dai|-dad]" " [-ext name] [-xlr|-xlg] host rnum\n", g_progname); fprintf(stderr, " %s misc [-port num] [-cnum num] [-tout num] host rnum\n", g_progname); fprintf(stderr, " %s wicked [-port num] [-cnum num] [-tout num] host rnum\n", g_progname); fprintf(stderr, " %s table [-port num] [-cnum num] [-tout num] [-exp num] host rnum\n", g_progname); fprintf(stderr, "\n"); exit(1); } /* print formatted information string and flush the buffer */ static void iprintf(const char *format, ...){ va_list ap; va_start(ap, format); vprintf(format, ap); fflush(stdout); va_end(ap); } /* print a character and flush the buffer */ static void iputchar(int c){ putchar(c); fflush(stdout); } /* print error message of abstract database */ static void eprint(TCRDB *rdb, int line, const char *func){ int ecode = tcrdbecode(rdb); fprintf(stderr, "%s: %d: %s: error: %d: %s\n", g_progname, line, func, ecode, tcrdberrmsg(ecode)); } /* get a random number */ static int myrand(int range){ if(range < 2) return 0; int high = (unsigned int)rand() >> 4; int low = range * (rand() / (RAND_MAX + 1.0)); low &= (unsigned int)INT_MAX >> 4; return (high + low) % range; } /* open the remote database */ static bool myopen(TCRDB *rdb, const char *host, int port){ bool err = false; if(strchr(host, ':') || strchr(host, '#')){ if(!tcrdbopen2(rdb, host)) err = true; } else { if(!tcrdbopen(rdb, host, port)) err = true; } return !err; } /* parse arguments of write command */ static int runwrite(int argc, char **argv){ char *host = NULL; char *rstr = NULL; int port = TTDEFPORT; int cnum = 1; int tout = 0; bool nr = false; bool rnd = false; for(int i = 2; i < argc; i++){ if(!host && argv[i][0] == '-'){ if(!strcmp(argv[i], "-port")){ if(++i >= argc) usage(); port = tcatoi(argv[i]); } else if(!strcmp(argv[i], "-cnum")){ if(++i >= argc) usage(); cnum = tcatoi(argv[i]); } else if(!strcmp(argv[i], "-tout")){ if(++i >= argc) usage(); tout = tcatoi(argv[i]); } else if(!strcmp(argv[i], "-nr")){ nr = true; } else if(!strcmp(argv[i], "-rnd")){ rnd = true; } else { usage(); } } else if(!host){ host = argv[i]; } else if(!rstr){ rstr = argv[i]; } else { usage(); } } if(!host || !rstr || cnum < 1) usage(); int rnum = tcatoi(rstr); if(rnum < 1) usage(); int rv = procwrite(host, port, cnum, tout, rnum, nr, rnd); return rv; } /* parse arguments of read command */ static int runread(int argc, char **argv){ char *host = NULL; int port = TTDEFPORT; int cnum = 1; int tout = 0; int mul = 0; bool rnd = false; for(int i = 2; i < argc; i++){ if(!host && argv[i][0] == '-'){ if(!strcmp(argv[i], "-port")){ if(++i >= argc) usage(); port = tcatoi(argv[i]); } else if(!strcmp(argv[i], "-cnum")){ if(++i >= argc) usage(); cnum = tcatoi(argv[i]); } else if(!strcmp(argv[i], "-tout")){ if(++i >= argc) usage(); tout = tcatoi(argv[i]); } else if(!strcmp(argv[i], "-mul")){ if(++i >= argc) usage(); mul = tcatoi(argv[i]); } else if(!strcmp(argv[i], "-rnd")){ rnd = true; } else { usage(); } } else if(!host){ host = argv[i]; } else { usage(); } } if(!host || cnum < 1) usage(); int rv = procread(host, port, cnum, tout, mul, rnd); return rv; } /* parse arguments of remove command */ static int runremove(int argc, char **argv){ char *host = NULL; int port = TTDEFPORT; int cnum = 1; int tout = 0; bool rnd = false; for(int i = 2; i < argc; i++){ if(!host && argv[i][0] == '-'){ if(!strcmp(argv[i], "-port")){ if(++i >= argc) usage(); port = tcatoi(argv[i]); } else if(!strcmp(argv[i], "-cnum")){ if(++i >= argc) usage(); cnum = tcatoi(argv[i]); } else if(!strcmp(argv[i], "-tout")){ if(++i >= argc) usage(); tout = tcatoi(argv[i]); } else if(!strcmp(argv[i], "-rnd")){ rnd = true; } else { usage(); } } else if(!host){ host = argv[i]; } else { usage(); } } if(!host || cnum < 1) usage(); int rv = procremove(host, port, cnum, tout, rnd); return rv; } /* parse arguments of rcat command */ static int runrcat(int argc, char **argv){ char *host = NULL; char *rstr = NULL; int port = TTDEFPORT; int cnum = 1; int tout = 0; int shl = 0; bool dai = false; bool dad = false; char *ext = NULL; int xopts = 0; for(int i = 2; i < argc; i++){ if(!host && argv[i][0] == '-'){ if(!strcmp(argv[i], "-port")){ if(++i >= argc) usage(); port = tcatoi(argv[i]); } else if(!strcmp(argv[i], "-cnum")){ if(++i >= argc) usage(); cnum = tcatoi(argv[i]); } else if(!strcmp(argv[i], "-tout")){ if(++i >= argc) usage(); tout = tcatoi(argv[i]); } else if(!strcmp(argv[i], "-shl")){ if(++i >= argc) usage(); shl = tcatoi(argv[i]); } else if(!strcmp(argv[i], "-dai")){ dai = true; } else if(!strcmp(argv[i], "-dad")){ dad = true; } else if(!strcmp(argv[i], "-ext")){ if(++i >= argc) usage(); ext = argv[i]; } else if(!strcmp(argv[i], "-xlr")){ xopts |= RDBXOLCKREC; } else if(!strcmp(argv[i], "-xlg")){ xopts |= RDBXOLCKGLB; } else { usage(); } } else if(!host){ host = argv[i]; } else if(!rstr){ rstr = argv[i]; } else { usage(); } } if(!host || !rstr || cnum < 1) usage(); int rnum = tcatoi(rstr); if(rnum < 1) usage(); int rv = procrcat(host, port, cnum, tout, rnum, shl, dai, dad, ext, xopts); return rv; } /* parse arguments of misc command */ static int runmisc(int argc, char **argv){ char *host = NULL; char *rstr = NULL; int port = TTDEFPORT; int cnum = 1; int tout = 0; for(int i = 2; i < argc; i++){ if(!host && argv[i][0] == '-'){ if(!strcmp(argv[i], "-port")){ if(++i >= argc) usage(); port = tcatoi(argv[i]); } else if(!strcmp(argv[i], "-cnum")){ if(++i >= argc) usage(); cnum = tcatoi(argv[i]); } else if(!strcmp(argv[i], "-tout")){ if(++i >= argc) usage(); tout = tcatoi(argv[i]); } else { usage(); } } else if(!host){ host = argv[i]; } else if(!rstr){ rstr = argv[i]; } else { usage(); } } if(!host || !rstr || cnum < 1) usage(); int rnum = tcatoi(rstr); if(rnum < 1) usage(); int rv = procmisc(host, port, cnum, tout, rnum); return rv; } /* parse arguments of wicked command */ static int runwicked(int argc, char **argv){ char *host = NULL; char *rstr = NULL; int port = TTDEFPORT; int cnum = 1; int tout = 0; for(int i = 2; i < argc; i++){ if(!host && argv[i][0] == '-'){ if(!strcmp(argv[i], "-port")){ if(++i >= argc) usage(); port = tcatoi(argv[i]); } else if(!strcmp(argv[i], "-cnum")){ if(++i >= argc) usage(); cnum = tcatoi(argv[i]); } else if(!strcmp(argv[i], "-tout")){ if(++i >= argc) usage(); tout = tcatoi(argv[i]); } else { usage(); } } else if(!host){ host = argv[i]; } else if(!rstr){ rstr = argv[i]; } else { usage(); } } if(!host || !rstr || cnum < 1) usage(); int rnum = tcatoi(rstr); if(rnum < 1) usage(); int rv = procwicked(host, port, cnum, tout, rnum); return rv; } /* parse arguments of table command */ static int runtable(int argc, char **argv){ char *host = NULL; char *rstr = NULL; int port = TTDEFPORT; int cnum = 1; int tout = 0; int exp = 0; for(int i = 2; i < argc; i++){ if(!host && argv[i][0] == '-'){ if(!strcmp(argv[i], "-port")){ if(++i >= argc) usage(); port = tcatoi(argv[i]); } else if(!strcmp(argv[i], "-cnum")){ if(++i >= argc) usage(); cnum = tcatoi(argv[i]); } else if(!strcmp(argv[i], "-tout")){ if(++i >= argc) usage(); tout = tcatoi(argv[i]); } else if(!strcmp(argv[i], "-exp")){ if(++i >= argc) usage(); exp = tcatoi(argv[i]); } else { usage(); } } else if(!host){ host = argv[i]; } else if(!rstr){ rstr = argv[i]; } else { usage(); } } if(!host || !rstr || cnum < 1) usage(); int rnum = tcatoi(rstr); if(rnum < 1) usage(); int rv = proctable(host, port, cnum, tout, rnum, exp); return rv; } /* perform write command */ static int procwrite(const char *host, int port, int cnum, int tout, int rnum, bool nr, bool rnd){ iprintf("\n host=%s port=%d cnum=%d tout=%d rnum=%d nr=%d rnd=%d\n\n", host, port, cnum, tout, rnum, nr, rnd); bool err = false; double stime = tctime(); TCRDB *rdbs[cnum]; for(int i = 0; i < cnum; i++){ rdbs[i] = tcrdbnew(); if(tout > 0) tcrdbtune(rdbs[i], tout, RDBTRECON); if(!myopen(rdbs[i], host, port)){ eprint(rdbs[i], __LINE__, "tcrdbopen"); err = true; } } TCRDB *rdb = rdbs[0]; if(!rnd && !tcrdbvanish(rdb)){ eprint(rdb, __LINE__, "tcrdbvanish"); err = true; } for(int i = 1; i <= rnum; i++){ char buf[RECBUFSIZ]; int len = sprintf(buf, "%08d", rnd ? myrand(rnum) + 1 : i); if(nr){ if(!tcrdbputnr(rdb, buf, len, buf, len)){ eprint(rdb, __LINE__, "tcrdbputnr"); err = true; break; } } else { if(!tcrdbput(rdb, buf, len, buf, len)){ eprint(rdb, __LINE__, "tcrdbput"); err = true; break; } } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); rdb = rdbs[myrand(rnum)%cnum]; } } iprintf("record number: %llu\n", (unsigned long long)tcrdbrnum(rdb)); iprintf("size: %llu\n", (unsigned long long)tcrdbsize(rdb)); for(int i = 0; i < cnum; i++){ if(!tcrdbclose(rdbs[i])){ eprint(rdbs[i], __LINE__, "tcrdbclose"); err = true; } tcrdbdel(rdbs[i]); } iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } /* perform read command */ static int procread(const char *host, int port, int cnum, int tout, int mul, bool rnd){ iprintf("\n host=%s port=%d cnum=%d tout=%d mul=%d rnd=%d\n\n", host, port, cnum, tout, mul, rnd); bool err = false; double stime = tctime(); TCRDB *rdbs[cnum]; for(int i = 0; i < cnum; i++){ rdbs[i] = tcrdbnew(); if(tout > 0) tcrdbtune(rdbs[i], tout, RDBTRECON); if(!myopen(rdbs[i], host, port)){ eprint(rdbs[i], __LINE__, "tcrdbopen"); err = true; } } TCRDB *rdb = rdbs[0]; TCMAP *recs = (mul > 1) ? tcmapnew() : NULL; int rnum = tcrdbrnum(rdb); for(int i = 1; i <= rnum; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%08d", rnd ? myrand(rnum) + 1 : i); if(mul > 1){ tcmapput(recs, kbuf, ksiz, kbuf, ksiz); if(i % mul == 0){ if(!tcrdbget3(rdb, recs)){ eprint(rdb, __LINE__, "tcrdbget3"); err = true; break; } tcmapclear(recs); } } else { int vsiz; char *vbuf = tcrdbget(rdb, kbuf, ksiz, &vsiz); if(!vbuf && !rnd){ eprint(rdb, __LINE__, "tcrdbget"); err = true; break; } tcfree(vbuf); } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); rdb = rdbs[myrand(rnum)%cnum]; } } if(recs) tcmapdel(recs); iprintf("record number: %llu\n", (unsigned long long)tcrdbrnum(rdb)); iprintf("size: %llu\n", (unsigned long long)tcrdbsize(rdb)); for(int i = 0; i < cnum; i++){ if(!tcrdbclose(rdbs[i])){ eprint(rdbs[i], __LINE__, "tcrdbclose"); err = true; } tcrdbdel(rdbs[i]); } iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } /* perform remove command */ static int procremove(const char *host, int port, int cnum, int tout, bool rnd){ iprintf("\n host=%s port=%d cnum=%d tout=%d rnd=%d\n\n", host, port, cnum, tout, rnd); bool err = false; double stime = tctime(); TCRDB *rdbs[cnum]; for(int i = 0; i < cnum; i++){ rdbs[i] = tcrdbnew(); if(tout > 0) tcrdbtune(rdbs[i], tout, RDBTRECON); if(!myopen(rdbs[i], host, port)){ eprint(rdbs[i], __LINE__, "tcrdbopen"); err = true; } } TCRDB *rdb = rdbs[0]; int rnum = tcrdbrnum(rdb); for(int i = 1; i <= rnum; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%08d", rnd ? myrand(rnum) + 1 : i); if(!tcrdbout(rdb, kbuf, ksiz) && !rnd){ eprint(rdb, __LINE__, "tcrdbout"); err = true; break; } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); rdb = rdbs[myrand(rnum)%cnum]; } } iprintf("record number: %llu\n", (unsigned long long)tcrdbrnum(rdb)); iprintf("size: %llu\n", (unsigned long long)tcrdbsize(rdb)); for(int i = 0; i < cnum; i++){ if(!tcrdbclose(rdbs[i])){ eprint(rdbs[i], __LINE__, "tcrdbclose"); err = true; } tcrdbdel(rdbs[i]); } iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } /* perform rcat command */ static int procrcat(const char *host, int port, int cnum, int tout, int rnum, int shl, bool dai, bool dad, const char *ext, int xopts){ iprintf("\n host=%s port=%d cnum=%d tout=%d rnum=%d" " shl=%d dai=%d dad=%d ext=%s xopts=%d\n\n", host, port, cnum, tout, rnum, shl, dai, dad, ext ? ext : "", xopts); int pnum = rnum / 5 + 1; bool err = false; double stime = tctime(); TCRDB *rdbs[cnum]; for(int i = 0; i < cnum; i++){ rdbs[i] = tcrdbnew(); if(tout > 0) tcrdbtune(rdbs[i], tout, RDBTRECON); if(!myopen(rdbs[i], host, port)){ eprint(rdbs[i], __LINE__, "tcrdbopen"); err = true; } } TCRDB *rdb = rdbs[0]; for(int i = 1; i <= rnum; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%d", myrand(pnum) + 1); if(shl > 0){ if(!tcrdbputshl(rdb, kbuf, ksiz, kbuf, ksiz, shl)){ eprint(rdb, __LINE__, "tcrdbputshl"); err = true; break; } } else if(dai){ if(tcrdbaddint(rdb, kbuf, ksiz, 1) == INT_MIN){ eprint(rdb, __LINE__, "tcrdbaddint"); err = true; break; } } else if(dad){ if(isnan(tcrdbadddouble(rdb, kbuf, ksiz, 1.0))){ eprint(rdb, __LINE__, "tcrdbadddouble"); err = true; break; } } else if(ext){ int xsiz; char *xbuf = tcrdbext(rdb, ext, xopts, kbuf, ksiz, kbuf, ksiz, &xsiz); if(!xbuf && tcrdbecode(rdb) != TTEMISC){ eprint(rdb, __LINE__, "tcrdbext"); err = true; break; } tcfree(xbuf); } else { if(!tcrdbputcat(rdb, kbuf, ksiz, kbuf, ksiz)){ eprint(rdb, __LINE__, "tcrdbputcat"); err = true; break; } } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); rdb = rdbs[myrand(rnum)%cnum]; } } iprintf("record number: %llu\n", (unsigned long long)tcrdbrnum(rdb)); iprintf("size: %llu\n", (unsigned long long)tcrdbsize(rdb)); for(int i = 0; i < cnum; i++){ if(!tcrdbclose(rdbs[i])){ eprint(rdbs[i], __LINE__, "tcrdbclose"); err = true; } tcrdbdel(rdbs[i]); } iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } /* perform misc command */ static int procmisc(const char *host, int port, int cnum, int tout, int rnum){ iprintf("\n host=%s port=%d cnum=%d tout=%d rnum=%d\n\n", host, port, cnum, tout, rnum); bool err = false; double stime = tctime(); TCRDB *rdbs[cnum]; for(int i = 0; i < cnum; i++){ rdbs[i] = tcrdbnew(); if(tout > 0) tcrdbtune(rdbs[i], tout, RDBTRECON); if(!myopen(rdbs[i], host, port)){ eprint(rdbs[i], __LINE__, "tcrdbopen"); err = true; } } TCRDB *rdb = rdbs[0]; if(!tcrdbvanish(rdb)){ eprint(rdb, __LINE__, "tcrdbvanish"); err = true; } iprintf("writing:\n"); for(int i = 1; i <= rnum; i++){ char buf[RECBUFSIZ]; int len = sprintf(buf, "%08d", i); if(myrand(10) > 0){ if(!tcrdbputkeep(rdb, buf, len, buf, len)){ eprint(rdb, __LINE__, "tcrdbputkeep"); err = true; break; } } else { if(!tcrdbputnr(rdb, buf, len, buf, len)){ eprint(rdb, __LINE__, "tcrdbputnr"); err = true; break; } } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); rdb = rdbs[myrand(rnum)%cnum]; } } for(int i = 0; i < cnum; i++){ if(tcrdbrnum(rdbs[i]) < 1){ eprint(rdb, __LINE__, "tcrdbrnum"); err = true; break; } } iprintf("reading:\n"); for(int i = 1; i <= rnum; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%08d", i); int vsiz; char *vbuf = tcrdbget(rdb, kbuf, ksiz, &vsiz); if(!vbuf){ eprint(rdb, __LINE__, "tcrdbget"); err = true; break; } else if(vsiz != ksiz || memcmp(vbuf, kbuf, vsiz)){ eprint(rdb, __LINE__, "(validation)"); err = true; tcfree(vbuf); break; } tcfree(vbuf); if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); rdb = rdbs[myrand(rnum)%cnum]; } } if(tcrdbrnum(rdb) != rnum){ eprint(rdb, __LINE__, "(validation)"); err = true; } iprintf("random writing:\n"); for(int i = 1; i <= rnum; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%d", myrand(rnum) + 1); char vbuf[RECBUFSIZ]; int vsiz = myrand(RECBUFSIZ); memset(vbuf, '*', vsiz); if(!tcrdbput(rdb, kbuf, ksiz, vbuf, vsiz)){ eprint(rdb, __LINE__, "tcrdbput"); err = true; break; } int rsiz; char *rbuf = tcrdbget(rdb, kbuf, ksiz, &rsiz); if(!rbuf){ eprint(rdb, __LINE__, "tcrdbget"); err = true; break; } if(rsiz != vsiz || memcmp(rbuf, vbuf, rsiz)){ eprint(rdb, __LINE__, "(validation)"); err = true; tcfree(rbuf); break; } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); rdb = rdbs[myrand(rnum)%cnum]; } tcfree(rbuf); } iprintf("word writing:\n"); const char *words[] = { "a", "A", "bb", "BB", "ccc", "CCC", "dddd", "DDDD", "eeeee", "EEEEEE", "mikio", "hirabayashi", "tokyo", "cabinet", "hyper", "estraier", "19780211", "birth day", "one", "first", "two", "second", "three", "third", "four", "fourth", "five", "fifth", "_[1]_", "uno", "_[2]_", "dos", "_[3]_", "tres", "_[4]_", "cuatro", "_[5]_", "cinco", "[\xe5\xb9\xb3\xe6\x9e\x97\xe5\xb9\xb9\xe9\x9b\x84]", "[\xe9\xa6\xac\xe9\xb9\xbf]", NULL }; for(int i = 0; words[i] != NULL; i += 2){ const char *kbuf = words[i]; int ksiz = strlen(kbuf); const char *vbuf = words[i+1]; int vsiz = strlen(vbuf); if(!tcrdbputkeep(rdb, kbuf, ksiz, vbuf, vsiz)){ eprint(rdb, __LINE__, "tcrdbputkeep"); err = true; break; } if(rnum > 250) putchar('.'); } if(rnum > 250) iprintf(" (%08d)\n", (int)(sizeof(words) / sizeof(*words))); iprintf("random erasing:\n"); for(int i = 1; i <= rnum; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%d", myrand(rnum)); if(!tcrdbout(rdb, kbuf, ksiz) && tcrdbecode(rdb) != TTENOREC){ eprint(rdb, __LINE__, "tcrdbout"); err = true; break; } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); rdb = rdbs[myrand(rnum)%cnum]; } } iprintf("writing:\n"); for(int i = 1; i <= rnum; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "[%d]", i); char vbuf[RECBUFSIZ]; int vsiz = i % RECBUFSIZ; memset(vbuf, '*', vsiz); if(!tcrdbputkeep(rdb, kbuf, ksiz, vbuf, vsiz)){ eprint(rdb, __LINE__, "tcrdbputkeep"); err = true; break; } if(vsiz < 1){ char tbuf[PATH_MAX]; for(int j = 0; j < PATH_MAX; j++){ tbuf[j] = myrand(0x100); } if(!tcrdbput(rdb, kbuf, ksiz, tbuf, PATH_MAX)){ eprint(rdb, __LINE__, "tcrdbput"); err = true; break; } } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); rdb = rdbs[myrand(rnum)%cnum]; } } iprintf("erasing:\n"); for(int i = 1; i <= rnum; i++){ if(i % 2 == 1){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "[%d]", i); if(!tcrdbout(rdb, kbuf, ksiz)){ eprint(rdb, __LINE__, "tcrdbout"); err = true; break; } tcrdbout(rdb, kbuf, ksiz); } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); rdb = rdbs[myrand(rnum)%cnum]; } } iprintf("random multi reading:\n"); for(int i = 1; i <= rnum; i++){ if(i % 10 == 1){ TCMAP *recs = tcmapnew(); int num = myrand(10); if(myrand(2) == 0){ char pbuf[RECBUFSIZ]; int psiz = sprintf(pbuf, "%d", myrand(100) + 1); TCLIST *keys = tcrdbfwmkeys(rdb, pbuf, psiz, num); for(int j = 0; j < tclistnum(keys); j++){ int ksiz; const char *kbuf = tclistval(keys, j, &ksiz); tcmapput(recs, kbuf, ksiz, kbuf, ksiz); } tclistdel(keys); } else { for(int j = 0; j < num; j++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%d", myrand(rnum) + 1); tcmapput(recs, kbuf, ksiz, kbuf, ksiz); } } if(tcrdbget3(rdb, recs)){ tcmapiterinit(recs); const char *kbuf; int ksiz; while((kbuf = tcmapiternext(recs, &ksiz))){ int vsiz; const char *vbuf = tcmapiterval(kbuf, &vsiz); int rsiz; char *rbuf = tcrdbget(rdb, kbuf, ksiz, &rsiz); if(rbuf){ if(rsiz != vsiz || memcmp(rbuf, vbuf, rsiz)){ eprint(rdb, __LINE__, "(validation)"); err = true; } tcfree(rbuf); } else { eprint(rdb, __LINE__, "tcrdbget"); err = true; } } } else { eprint(rdb, __LINE__, "tcrdbget3"); err = true; } tcmapdel(recs); } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); rdb = rdbs[myrand(rnum)%cnum]; } } iprintf("script extension calling:\n"); for(int i = 1; i <= rnum; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "(%d)", i); const char *name; switch(myrand(7)){ default: name = "put"; break; case 1: name = "putkeep"; break; case 2: name = "putcat"; break; case 3: name = "out"; break; case 4: name = "get"; break; case 5: name = "iterinit"; break; case 6: name = "iternext"; break; } int vsiz; char *vbuf = tcrdbext(rdb, name, 0, kbuf, ksiz, kbuf, ksiz, &vsiz); if(!vbuf && tcrdbecode(rdb) != TTEMISC){ eprint(rdb, __LINE__, "tcrdbext"); err = true; break; } tcfree(vbuf); if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); rdb = rdbs[myrand(rnum)%cnum]; } } iprintf("checking versatile functions:\n"); TCLIST *args = tclistnew(); for(int i = 1; i <= rnum; i++){ if(myrand(10) == 0){ const char *name; switch(myrand(3)){ default: name = "putlist"; break; case 1: name = "outlist"; break; case 2: name = "getlist"; break; } TCLIST *res = tcrdbmisc(rdb, name, 0, args); if(res){ tclistdel(res); } else { eprint(rdb, __LINE__, "tcrdbmisc"); err = true; break; } tclistclear(args); } else { char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "(%d)", myrand(rnum)); tclistpush(args, kbuf, ksiz); } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } tclistdel(args); char *sbuf = tcrdbstat(rdb); if(sbuf){ tcfree(sbuf); } else { eprint(rdb, __LINE__, "tcrdbstat"); err = true; } if(!tcrdbsync(rdb)){ eprint(rdb, __LINE__, "tcrdbsync"); err = true; } if(!tcrdboptimize(rdb, NULL)){ eprint(rdb, __LINE__, "tcrdbsync"); err = true; } if(!tcrdbvanish(rdb)){ eprint(rdb, __LINE__, "tcrdbvanish"); err = true; } iprintf("record number: %llu\n", (unsigned long long)tcrdbrnum(rdb)); iprintf("size: %llu\n", (unsigned long long)tcrdbsize(rdb)); for(int i = 0; i < cnum; i++){ if(!tcrdbclose(rdbs[i])){ eprint(rdbs[i], __LINE__, "tcrdbclose"); err = true; } tcrdbdel(rdbs[i]); } iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } /* perform wicked command */ static int procwicked(const char *host, int port, int cnum, int tout, int rnum){ iprintf("\n host=%s port=%d cnum=%d tout=%d rnum=%d\n\n", host, port, cnum, tout, rnum); bool err = false; double stime = tctime(); TCRDB *rdbs[cnum]; for(int i = 0; i < cnum; i++){ rdbs[i] = tcrdbnew(); if(tout > 0) tcrdbtune(rdbs[i], tout, RDBTRECON); if(!myopen(rdbs[i], host, port)){ eprint(rdbs[i], __LINE__, "tcrdbopen"); err = true; } } TCRDB *rdb = rdbs[0]; if(!tcrdbvanish(rdb)){ eprint(rdb, __LINE__, "tcrdbvanish"); err = true; } TCMAP *map = tcmapnew2(rnum / 5); for(int i = 1; i <= rnum && !err; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%d", myrand(rnum)); char vbuf[RECBUFSIZ]; int vsiz = myrand(RECBUFSIZ); memset(vbuf, '*', vsiz); vbuf[vsiz] = '\0'; char *rbuf; switch(myrand(16)){ case 0: putchar('0'); if(!tcrdbput(rdb, kbuf, ksiz, vbuf, vsiz)){ eprint(rdb, __LINE__, "tcrdbput"); err = true; } tcmapput(map, kbuf, ksiz, vbuf, vsiz); break; case 1: putchar('1'); if(!tcrdbput2(rdb, kbuf, vbuf)){ eprint(rdb, __LINE__, "tcrdbput2"); err = true; } tcmapput2(map, kbuf, vbuf); break; case 2: putchar('2'); tcrdbputkeep(rdb, kbuf, ksiz, vbuf, vsiz); tcmapputkeep(map, kbuf, ksiz, vbuf, vsiz); break; case 3: putchar('3'); tcrdbputkeep2(rdb, kbuf, vbuf); tcmapputkeep2(map, kbuf, vbuf); break; case 4: putchar('4'); if(!tcrdbputcat(rdb, kbuf, ksiz, vbuf, vsiz)){ eprint(rdb, __LINE__, "tcrdbputcat"); err = true; } tcmapputcat(map, kbuf, ksiz, vbuf, vsiz); break; case 5: putchar('5'); if(!tcrdbputcat2(rdb, kbuf, vbuf)){ eprint(rdb, __LINE__, "tcrdbputcat2"); err = true; } tcmapputcat2(map, kbuf, vbuf); break; case 6: putchar('6'); if(myrand(10) == 0){ if(!tcrdbputnr(rdb, kbuf, ksiz, vbuf, vsiz)){ eprint(rdb, __LINE__, "tcrdbputcat"); err = true; } if(tcrdbrnum(rdb) < 1){ eprint(rdb, __LINE__, "tcrdbrnum"); err = true; } tcmapput(map, kbuf, ksiz, vbuf, vsiz); } break; case 7: putchar('7'); if(myrand(10) == 0){ if(!tcrdbputnr2(rdb, kbuf, vbuf)){ eprint(rdb, __LINE__, "tcrdbputcat2"); err = true; } if(tcrdbrnum(rdb) < 1){ eprint(rdb, __LINE__, "tcrdbrnum"); err = true; } tcmapput2(map, kbuf, vbuf); } break; case 8: putchar('8'); if(myrand(10) == 0){ tcrdbout(rdb, kbuf, ksiz); tcmapout(map, kbuf, ksiz); } break; case 9: putchar('9'); if(myrand(10) == 0){ tcrdbout2(rdb, kbuf); tcmapout2(map, kbuf); } break; case 10: putchar('A'); if((rbuf = tcrdbget(rdb, kbuf, ksiz, &vsiz)) != NULL) tcfree(rbuf); break; case 11: putchar('B'); if((rbuf = tcrdbget2(rdb, kbuf)) != NULL) tcfree(rbuf); break; case 12: putchar('C'); tcrdbvsiz(rdb, kbuf, ksiz); break; case 13: putchar('D'); tcrdbvsiz2(rdb, kbuf); break; case 14: putchar('E'); if(myrand(rnum / 50) == 0){ if(!tcrdbiterinit(rdb)){ eprint(rdb, __LINE__, "tcrdbiterinit"); err = true; } } for(int j = myrand(rnum) / 1000 + 1; j >= 0; j--){ int iksiz; char *ikbuf = tcrdbiternext(rdb, &iksiz); if(ikbuf) tcfree(ikbuf); } break; default: putchar('@'); if(myrand(10000) == 0) srand((unsigned int)(tctime() * 1000) % UINT_MAX); rdb = rdbs[myrand(rnum)%cnum]; break; } if(i % 50 == 0) iprintf(" (%08d)\n", i); } if(rnum % 50 > 0) iprintf(" (%08d)\n", rnum); tcrdbsync(rdb); if(tcrdbrnum(rdb) != tcmaprnum(map)){ eprint(rdb, __LINE__, "(validation)"); err = true; } for(int i = 1; i <= rnum && !err; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%d", i - 1); int vsiz; const char *vbuf = tcmapget(map, kbuf, ksiz, &vsiz); int rsiz; char *rbuf = tcrdbget(rdb, kbuf, ksiz, &rsiz); if(vbuf){ putchar('.'); if(!rbuf){ eprint(rdb, __LINE__, "tcrdbget"); err = true; } else if(rsiz != vsiz || memcmp(rbuf, vbuf, rsiz)){ eprint(rdb, __LINE__, "(validation)"); err = true; } } else { putchar('*'); if(rbuf){ eprint(rdb, __LINE__, "(validation)"); err = true; } } tcfree(rbuf); if(i % 50 == 0) iprintf(" (%08d)\n", i); } if(rnum % 50 > 0) iprintf(" (%08d)\n", rnum); tcmapiterinit(map); int ksiz; const char *kbuf; for(int i = 1; (kbuf = tcmapiternext(map, &ksiz)) != NULL; i++){ putchar('+'); int vsiz; const char *vbuf = tcmapiterval(kbuf, &vsiz); int rsiz; char *rbuf = tcrdbget(rdb, kbuf, ksiz, &rsiz); if(!rbuf){ eprint(rdb, __LINE__, "tcrdbget"); err = true; } else if(rsiz != vsiz || memcmp(rbuf, vbuf, rsiz)){ eprint(rdb, __LINE__, "(validation)"); err = true; } tcfree(rbuf); if(!tcrdbout(rdb, kbuf, ksiz)){ eprint(rdb, __LINE__, "tcrdbout"); err = true; } if(i % 50 == 0) iprintf(" (%08d)\n", i); } int mrnum = tcmaprnum(map); if(mrnum % 50 > 0) iprintf(" (%08d)\n", mrnum); if(tcrdbrnum(rdb) != 0){ eprint(rdb, __LINE__, "(validation)"); err = true; } iprintf("record number: %llu\n", (unsigned long long)tcrdbrnum(rdb)); iprintf("size: %llu\n", (unsigned long long)tcrdbsize(rdb)); tcmapdel(map); for(int i = 0; i < cnum; i++){ if(!tcrdbclose(rdbs[i])){ eprint(rdbs[i], __LINE__, "tcrdbclose"); err = true; } tcrdbdel(rdbs[i]); } iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } /* perform table command */ static int proctable(const char *host, int port, int cnum, int tout, int rnum, int exp){ iprintf("
    \n host=%s port=%d cnum=%d tout=%d rnum=%d exp=%d\n\n", host, port, cnum, tout, rnum, exp); bool err = false; double stime = tctime(); TCRDB *rdbs[cnum]; for(int i = 0; i < cnum; i++){ rdbs[i] = tcrdbnew(); if(tout > 0) tcrdbtune(rdbs[i], tout, RDBTRECON); if(!myopen(rdbs[i], host, port)){ eprint(rdbs[i], __LINE__, "tcrdbopen"); err = true; } } TCRDB *rdb = rdbs[0]; if(!tcrdbvanish(rdb)){ eprint(rdb, __LINE__, "tcrdbvanish"); err = true; } if(exp > 0){ if(!tcrdbtblsetindex(rdb, "x", RDBITDECIMAL)){ eprint(rdb, __LINE__, "tcrdbtblsetindex"); err = true; } for(int i = 1; i <= rnum && !err; i++){ char pkbuf[RECBUFSIZ]; int pksiz = sprintf(pkbuf, "%d", myrand(rnum) + 1); int act = myrand(100); if(act < 50){ TCMAP *cols = tcmapnew2(7); char vbuf[RECBUFSIZ*5]; int vsiz = sprintf(vbuf, "%08d", i); tcmapput(cols, "n", 1, vbuf, vsiz); vsiz = sprintf(vbuf, "%d", (int)tctime() + exp); tcmapput(cols, "x", 1, vbuf, vsiz); if(!tcrdbtblput(rdb, pkbuf, pksiz, cols)){ eprint(rdb, __LINE__, "tcrdbtblput"); err = true; } tcmapdel(cols); } else if(act == 60){ if(!tcrdbtblout(rdb, pkbuf, pksiz) && tcrdbecode(rdb) != TTENOREC){ eprint(rdb, __LINE__, "tcrdbtblout"); err = true; } } else { TCMAP *cols = tcrdbtblget(rdb, pkbuf, pksiz); if(cols){ tcmapdel(cols); } else if(tcrdbecode(rdb) != TTENOREC){ eprint(rdb, __LINE__, "tcrdbtblget"); err = true; } } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); rdb = rdbs[myrand(rnum)%cnum]; } } } else { if(!tcrdbtblsetindex(rdb, "", RDBITDECIMAL)){ eprint(rdb, __LINE__, "tcrdbtblsetindex"); err = true; } if(!tcrdbtblsetindex(rdb, "str", RDBITLEXICAL)){ eprint(rdb, __LINE__, "tcrdbtblsetindex"); err = true; } if(!tcrdbtblsetindex(rdb, "num", RDBITDECIMAL)){ eprint(rdb, __LINE__, "tcrdbtblsetindex"); err = true; } if(!tcrdbtblsetindex(rdb, "type", RDBITDECIMAL)){ eprint(rdb, __LINE__, "tcrdbtblsetindex"); err = true; } if(!tcrdbtblsetindex(rdb, "flag", RDBITTOKEN)){ eprint(rdb, __LINE__, "tcrdbtblsetindex"); err = true; } if(!tcrdbtblsetindex(rdb, "text", RDBITQGRAM)){ eprint(rdb, __LINE__, "tcrdbtblsetindex"); err = true; } iprintf("writing:\n"); for(int i = 1; i <= rnum && !err; i++){ int id = (int)tcrdbtblgenuid(rdb); char pkbuf[RECBUFSIZ]; int pksiz = sprintf(pkbuf, "%d", id); TCMAP *cols = tcmapnew2(7); char vbuf[RECBUFSIZ*5]; int vsiz = sprintf(vbuf, "%d", id); tcmapput(cols, "str", 3, vbuf, vsiz); vsiz = sprintf(vbuf, "%d", myrand(i) + 1); tcmapput(cols, "num", 3, vbuf, vsiz); vsiz = sprintf(vbuf, "%d", myrand(32) + 1); tcmapput(cols, "type", 4, vbuf, vsiz); int num = myrand(5); int pt = 0; char *wp = vbuf; for(int j = 0; j < num; j++){ pt += myrand(5) + 1; if(wp > vbuf) *(wp++) = ','; wp += sprintf(wp, "%d", pt); } *wp = '\0'; if(*vbuf != '\0'){ tcmapput(cols, "flag", 4, vbuf, wp - vbuf); tcmapput(cols, "text", 4, vbuf, wp - vbuf); } switch(myrand(4)){ default: if(!tcrdbtblput(rdb, pkbuf, pksiz, cols)){ eprint(rdb, __LINE__, "tcrdbtblput"); err = true; } break; case 1: if(!tcrdbtblputkeep(rdb, pkbuf, pksiz, cols)){ eprint(rdb, __LINE__, "tcrdbtblputkeep"); err = true; } break; case 2: if(!tcrdbtblputcat(rdb, pkbuf, pksiz, cols)){ eprint(rdb, __LINE__, "tcrdbtblput"); err = true; } break; } tcmapdel(cols); if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); rdb = rdbs[myrand(rnum)%cnum]; } } iprintf("removing:\n"); for(int i = 1; i <= rnum && !err; i++){ char pkbuf[RECBUFSIZ]; int pksiz = sprintf(pkbuf, "%d", myrand(rnum) + 1); if(!tcrdbtblout(rdb, pkbuf, pksiz) && tcrdbecode(rdb) != TTENOREC){ eprint(rdb, __LINE__, "tcrdbtblout"); err = true; } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); rdb = rdbs[myrand(rnum)%cnum]; } } iprintf("reading:\n"); for(int i = 1; i <= rnum && !err; i++){ char pkbuf[RECBUFSIZ]; int pksiz = sprintf(pkbuf, "%d", myrand(rnum) + 1); TCMAP *cols = tcrdbtblget(rdb, pkbuf, pksiz); if(cols){ tcmapdel(cols); } else if(tcrdbecode(rdb) != TTENOREC){ eprint(rdb, __LINE__, "tcrdbtblget"); err = true; } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); rdb = rdbs[myrand(rnum)%cnum]; } } iprintf("searching:\n"); const char *names[] = { "", "str", "num", "type", "flag", "text", "c1" }; int ops[] = { RDBQCSTREQ, RDBQCSTRINC, RDBQCSTRBW, RDBQCSTREW, RDBQCSTRAND, RDBQCSTROR, RDBQCSTROREQ, RDBQCSTRRX, RDBQCNUMEQ, RDBQCNUMGT, RDBQCNUMGE, RDBQCNUMLT, RDBQCNUMLE, RDBQCNUMBT, RDBQCNUMOREQ }; int ftsops[] = { RDBQCFTSPH, RDBQCFTSAND, RDBQCFTSOR, RDBQCFTSEX }; int types[] = { RDBQOSTRASC, RDBQOSTRDESC, RDBQONUMASC, RDBQONUMDESC }; for(int i = 1; i <= rnum && !err; i++){ RDBQRY *qry = tcrdbqrynew(rdb); int condnum = myrand(4); if(condnum < 1 && myrand(5) != 0) condnum = 1; for(int j = 0; j < condnum; j++){ const char *name = names[myrand(sizeof(names) / sizeof(*names))]; int op = ops[myrand(sizeof(ops) / sizeof(*ops))]; if(myrand(10) == 0) op = ftsops[myrand(sizeof(ftsops) / sizeof(*ftsops))]; if(myrand(20) == 0) op |= RDBQCNEGATE; if(myrand(20) == 0) op |= RDBQCNOIDX; char expr[RECBUFSIZ*3]; char *wp = expr; wp += sprintf(expr, "%d", myrand(i)); if(myrand(10) == 0) wp += sprintf(wp, ",%d", myrand(i)); if(myrand(10) == 0) wp += sprintf(wp, ",%d", myrand(i)); tcrdbqryaddcond(qry, name, op, expr); } if(myrand(3) != 0){ const char *name = names[myrand(sizeof(names) / sizeof(*names))]; int type = types[myrand(sizeof(types) / sizeof(*types))]; tcrdbqrysetorder(qry, name, type); } tcrdbqrysetlimit(qry, myrand(10), myrand(5) * 10); if(myrand(10) == 0){ RDBQRY *qrys[4]; int num = myrand(5); for(int j = 0; j < num; j++){ qrys[j] = qry; } if(myrand(3) == 0){ TCLIST *res = tcrdbparasearch(qrys, num); for(int j = 0; j < 3 && j < tclistnum(res); j++){ TCMAP *cols = tcrdbqryrescols(res, j); if(!tcmapget2(cols, "")){ eprint(rdb, __LINE__, "(validation)"); err = true; } tcmapdel(cols); } tclistdel(res); } else { TCLIST *res = tcrdbmetasearch(qrys, num, RDBMSUNION + myrand(3)); for(int j = 0; j < 3 && j < tclistnum(res); j++){ int pksiz; const char *pkbuf = tclistval(res, j, &pksiz); TCMAP *cols = tcrdbtblget(rdb, pkbuf, pksiz); if(cols){ tcmapdel(cols); } else { eprint(rdb, __LINE__, "tcrdbtblget"); err = true; } } tclistdel(res); } } else { TCLIST *res = tcrdbqrysearch(qry); tclistdel(res); } tcrdbqrydel(qry); if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); rdb = rdbs[myrand(rnum)%cnum]; } } RDBQRY *qry = tcrdbqrynew(rdb); tcrdbqryaddcond(qry, "", RDBQCSTRBW, "1"); tcrdbqrysetorder(qry, "str", RDBQOSTRASC); tcrdbqrysetlimit(qry, 10, 0); TCLIST *res = tcrdbqrysearchget(qry); for(int i = 0; i < tclistnum(res); i++){ TCMAP *cols = tcrdbqryrescols(res, i); if(!tcmapget2(cols, "")){ eprint(rdbs[i], __LINE__, "(validation)"); err = true; break; } tcmapdel(cols); } if(tclistnum(res) != tcrdbqrysearchcount(qry)){ eprint(rdb, __LINE__, "(validation)"); err = true; } tclistdel(res); tcrdbqrydel(qry); qry = tcrdbqrynew(rdb); res = tcrdbqrysearch(qry); if(tclistnum(res) != tcrdbrnum(rdb)){ eprint(rdb, __LINE__, "(validation)"); err = true; } tclistdel(res); tcrdbqrysearchout(qry); if(tcrdbrnum(rdb) != 0){ eprint(rdb, __LINE__, "(validation)"); err = true; } tcrdbqrydel(qry); } iprintf("record number: %llu\n", (unsigned long long)tcrdbrnum(rdb)); iprintf("size: %llu\n", (unsigned long long)tcrdbsize(rdb)); for(int i = 0; i < cnum; i++){ if(!tcrdbclose(rdbs[i])){ eprint(rdbs[i], __LINE__, "tcrdbclose"); err = true; } tcrdbdel(rdbs[i]); } iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } // END OF FILE tokyotyrant-1.1.40/tculog.c000066400000000000000000001071561133351147200156570ustar00rootroot00000000000000/************************************************************************************************* * The update log API of Tokyo Tyrant * Copyright (C) 2006-2010 Mikio Hirabayashi * This file is part of Tokyo Tyrant. * Tokyo Tyrant is free software; you can redistribute it and/or modify it under the terms of * the GNU Lesser General Public License as published by the Free Software Foundation; either * version 2.1 of the License or any later version. Tokyo Tyrant 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 Lesser General Public * License for more details. * You should have received a copy of the GNU Lesser General Public License along with Tokyo * Tyrant; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA. *************************************************************************************************/ #include "ttutil.h" #include "tculog.h" #include "myconf.h" #define TCULAIOCBNUM 64 // number of AIO tasks #define TCULTMDEVALW 30.0 // allowed time deviance #define TCREPLTIMEO 60.0 // timeout of the replication socket typedef struct { // type of structure for a putshl operand const char *vbuf; // region of the value. int vsiz; // size of the region int width; // the width of the record } PUTSHLOP; /* private function prototypes */ static bool tculogflushaiocbp(struct aiocb *aiocbp); static void *tculogadbputshlproc(const void *vbuf, int vsiz, int *sp, PUTSHLOP *op); /************************************************************************************************* * API *************************************************************************************************/ /* Create an update log object. */ TCULOG *tculognew(void){ TCULOG *ulog = tcmalloc(sizeof(*ulog)); for(int i = 0; i < TCULRMTXNUM; i++){ if(pthread_mutex_init(ulog->rmtxs + i, NULL) != 0) tcmyfatal("pthread_mutex_init failed"); } if(pthread_rwlock_init(&ulog->rwlck, NULL) != 0) tcmyfatal("pthread_rwlock_init failed"); if(pthread_cond_init(&ulog->cnd, NULL) != 0) tcmyfatal("pthread_cond_init failed"); if(pthread_mutex_init(&ulog->wmtx, NULL) != 0) tcmyfatal("pthread_mutex_init failed"); ulog->base = NULL; ulog->limsiz = 0; ulog->max = 0; ulog->fd = -1; ulog->size = 0; ulog->aiocbs = NULL; ulog->aiocbi = 0; ulog->aioend = 0; return ulog; } /* Delete an update log object. */ void tculogdel(TCULOG *ulog){ assert(ulog); if(ulog->base) tculogclose(ulog); if(ulog->aiocbs) tcfree(ulog->aiocbs); pthread_mutex_destroy(&ulog->wmtx); pthread_cond_destroy(&ulog->cnd); pthread_rwlock_destroy(&ulog->rwlck); for(int i = TCULRMTXNUM - 1; i >= 0; i--){ pthread_mutex_destroy(ulog->rmtxs + i); } tcfree(ulog); } /* Set AIO control of an update log object. */ bool tculogsetaio(TCULOG *ulog){ #if defined(_SYS_LINUX_) assert(ulog); if(ulog->base || ulog->aiocbs) return false; struct aiocb *aiocbs = tcmalloc(sizeof(*aiocbs) * TCULAIOCBNUM); for(int i = 0; i < TCULAIOCBNUM; i++){ memset(aiocbs + i, 0, sizeof(*aiocbs)); } ulog->aiocbs = aiocbs; return true; #else assert(ulog); return true; #endif } /* Open files of an update log object. */ bool tculogopen(TCULOG *ulog, const char *base, uint64_t limsiz){ assert(ulog && base); if(ulog->base) return false; struct stat sbuf; if(stat(base, &sbuf) == -1 || !S_ISDIR(sbuf.st_mode)) return false; TCLIST *names = tcreaddir(base); if(!names) return false; int ln = tclistnum(names); int max = 0; for(int i = 0; i < ln; i++){ const char *name = tclistval2(names, i); if(!tcstrbwm(name, TCULSUFFIX)) continue; int id = tcatoi(name); char *path = tcsprintf("%s/%08d%s", base, id, TCULSUFFIX); if(stat(path, &sbuf) == 0 && S_ISREG(sbuf.st_mode) && id > max) max = id; tcfree(path); } tclistdel(names); if(max < 1) max = 1; ulog->base = tcstrdup(base); ulog->limsiz = (limsiz > 0) ? limsiz : INT64_MAX / 2; ulog->max = max; ulog->fd = -1; ulog->size = sbuf.st_size; struct aiocb *aiocbs = ulog->aiocbs; if(aiocbs){ for(int i = 0; i < TCULAIOCBNUM; i++){ struct aiocb *aiocbp = aiocbs + i; aiocbp->aio_fildes = 0; aiocbp->aio_buf = NULL; aiocbp->aio_nbytes = 0; } } ulog->aiocbi = 0; ulog->aioend = 0; return true; } /* Close files of an update log object. */ bool tculogclose(TCULOG *ulog){ assert(ulog); if(!ulog->base) return false; bool err = false; struct aiocb *aiocbs = ulog->aiocbs; if(aiocbs){ for(int i = 0; i < TCULAIOCBNUM; i++){ if(!tculogflushaiocbp(aiocbs + i)) err = true; } } if(ulog->fd != -1 && close(ulog->fd) != 0) err = true; tcfree(ulog->base); ulog->base = NULL; return !err; } /* Get the mutex index of a record. */ int tculogrmtxidx(TCULOG *ulog, const char *kbuf, int ksiz){ assert(ulog && kbuf && ksiz >= 0); if(!ulog->base || !ulog->aiocbs) return 0; uint32_t hash = 19780211; while(ksiz--){ hash = hash * 41 + *(uint8_t *)kbuf++; } return hash % TCULRMTXNUM; } /* Begin the critical section of an update log object. */ bool tculogbegin(TCULOG *ulog, int idx){ assert(ulog); if(!ulog->base) return false; if(idx < 0){ for(int i = 0; i < TCULRMTXNUM; i++){ if(pthread_mutex_lock(ulog->rmtxs + i) != 0){ for(i--; i >= 0; i--){ pthread_mutex_unlock(ulog->rmtxs + i); } return false; } } return true; } return pthread_mutex_lock(ulog->rmtxs + idx) == 0; } /* End the critical section of an update log object. */ bool tculogend(TCULOG *ulog, int idx){ assert(ulog); if(idx < 0){ bool err = false; for(int i = TCULRMTXNUM - 1; i >= 0; i--){ if(pthread_mutex_unlock(ulog->rmtxs + i) != 0) err = true; } return !err; } return pthread_mutex_unlock(ulog->rmtxs + idx) == 0; } /* Write a message into an update log object. */ bool tculogwrite(TCULOG *ulog, uint64_t ts, uint32_t sid, uint32_t mid, const void *ptr, int size){ assert(ulog && ptr && size >= 0); if(!ulog->base) return false; if(ts < 1) ts = (uint64_t)(tctime() * 1000000); bool err = false; if(pthread_rwlock_wrlock(&ulog->rwlck) != 0) return false; pthread_cleanup_push((void (*)(void *))pthread_rwlock_unlock, &ulog->rwlck); if(ulog->fd == -1){ char *path = tcsprintf("%s/%08d%s", ulog->base, ulog->max, TCULSUFFIX); int fd = open(path, O_WRONLY | O_CREAT | O_APPEND, 00644); tcfree(path); struct stat sbuf; if(fd != -1 && fstat(fd, &sbuf) == 0){ ulog->fd = fd; ulog->size = sbuf.st_size; } else { err = true; } } int rsiz = sizeof(uint8_t) + sizeof(uint64_t) + sizeof(uint32_t) * 2 + size; unsigned char stack[TTIOBUFSIZ]; unsigned char *buf = (rsiz < TTIOBUFSIZ) ? stack : tcmalloc(rsiz); pthread_cleanup_push(free, (buf == stack) ? NULL : buf); unsigned char *wp = buf; *(wp++) = TCULMAGICNUM; uint64_t llnum = TTHTONLL(ts); memcpy(wp, &llnum, sizeof(llnum)); wp += sizeof(llnum); uint16_t snum = TTHTONS(sid); memcpy(wp, &snum, sizeof(snum)); wp += sizeof(snum); snum = TTHTONS(mid); memcpy(wp, &snum, sizeof(snum)); wp += sizeof(snum); uint32_t lnum = TTHTONL(size); memcpy(wp, &lnum, sizeof(lnum)); wp += sizeof(lnum); memcpy(wp, ptr, size); if(ulog->fd != -1){ struct aiocb *aiocbs = (struct aiocb *)ulog->aiocbs; if(aiocbs){ struct aiocb *aiocbp = aiocbs + ulog->aiocbi; if(aiocbp->aio_buf){ off_t aioend = aiocbp->aio_offset + aiocbp->aio_nbytes; if(tculogflushaiocbp(aiocbp)){ ulog->aioend = aioend; } else { err = true; } } aiocbp->aio_fildes = ulog->fd; aiocbp->aio_offset = ulog->size; aiocbp->aio_buf = tcmemdup(buf, rsiz); aiocbp->aio_nbytes = rsiz; while(aio_write(aiocbp) != 0){ if(errno != EAGAIN){ tcfree((char *)aiocbp->aio_buf); aiocbp->aio_buf = NULL; err = true; break; } for(int i = 0; i < TCULAIOCBNUM; i++){ if(i == ulog->aiocbi) continue; if(!tculogflushaiocbp(aiocbs + i)){ err = true; break; } } } ulog->aiocbi = (ulog->aiocbi + 1) % TCULAIOCBNUM; } else { if(!tcwrite(ulog->fd, buf, rsiz)) err = true; } if(!err){ ulog->size += rsiz; if(ulog->size >= ulog->limsiz){ if(aiocbs){ for(int i = 0; i < TCULAIOCBNUM; i++){ if(!tculogflushaiocbp(aiocbs + i)) err = true; } ulog->aiocbi = 0; ulog->aioend = 0; } char *path = tcsprintf("%s/%08d%s", ulog->base, ulog->max + 1, TCULSUFFIX); int fd = open(path, O_WRONLY | O_CREAT | O_APPEND, 00644); tcfree(path); if(fd != 0){ if(close(ulog->fd) != 0) err = true; ulog->fd = fd; ulog->size = 0; ulog->max++; } else { err = true; } } if(pthread_cond_broadcast(&ulog->cnd) != 0) err = true; } } else { err = true; } pthread_cleanup_pop(1); pthread_cleanup_pop(1); return !err; } /* Create a log reader object. */ TCULRD *tculrdnew(TCULOG *ulog, uint64_t ts){ assert(ulog); if(!ulog->base) return NULL; if(pthread_rwlock_rdlock(&ulog->rwlck) != 0) return NULL; TCLIST *names = tcreaddir(ulog->base); if(!names){ pthread_rwlock_unlock(&ulog->rwlck); return NULL; } int ln = tclistnum(names); int max = 0; for(int i = 0; i < ln; i++){ const char *name = tclistval2(names, i); if(!tcstrbwm(name, TCULSUFFIX)) continue; int id = tcatoi(name); char *path = tcsprintf("%s/%08d%s", ulog->base, id, TCULSUFFIX); struct stat sbuf; if(stat(path, &sbuf) == 0 && S_ISREG(sbuf.st_mode) && id > max) max = id; tcfree(path); } tclistdel(names); if(max < 1) max = 1; uint64_t bts = (ts > TCULTMDEVALW * 1000000) ? ts - TCULTMDEVALW * 1000000 : 0; int num = 0; for(int i = max; i > 0; i--){ char *path = tcsprintf("%s/%08d%s", ulog->base, i, TCULSUFFIX); int fd = open(path, O_RDONLY, 00644); tcfree(path); if(fd == -1) break; int rsiz = sizeof(uint8_t) + sizeof(uint64_t); unsigned char buf[rsiz]; uint64_t fts = INT64_MAX; if(tcread(fd, buf, rsiz)){ memcpy(&fts, buf + sizeof(uint8_t), sizeof(ts)); fts = TTNTOHLL(fts); } close(fd); num = i; if(bts >= fts) break; } if(num < 1) num = 1; TCULRD *urld = tcmalloc(sizeof(*urld)); urld->ulog = ulog; urld->ts = ts; urld->num = num; urld->fd = -1; urld->rbuf = tcmalloc(TTIOBUFSIZ); urld->rsiz = TTIOBUFSIZ; pthread_rwlock_unlock(&ulog->rwlck); return urld; } /* Delete a log reader object. */ void tculrddel(TCULRD *ulrd){ assert(ulrd); if(ulrd->fd != -1) close(ulrd->fd); tcfree(ulrd->rbuf); tcfree(ulrd); } /* Wait the next message is written. */ void tculrdwait(TCULRD *ulrd){ assert(ulrd); TCULOG *ulog = ulrd->ulog; if(pthread_mutex_lock(&ulog->wmtx) != 0) return; pthread_cleanup_push((void (*)(void *))pthread_mutex_unlock, &ulog->wmtx); int ocs = PTHREAD_CANCEL_DISABLE; pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &ocs); struct timeval tv; struct timespec ts; if(gettimeofday(&tv, NULL) == 0){ ts.tv_sec = tv.tv_sec + 1; ts.tv_nsec = tv.tv_usec * 1000; } else { ts.tv_sec = (1ULL << (sizeof(time_t) * 8 - 1)) - 1; ts.tv_nsec = 0; } pthread_cond_timedwait(&ulog->cnd, &ulog->wmtx, &ts); pthread_setcancelstate(ocs, NULL); pthread_cleanup_pop(1); } /* Read a message from a log reader object. */ const void *tculrdread(TCULRD *ulrd, int *sp, uint64_t *tsp, uint32_t *sidp, uint32_t *midp){ assert(ulrd && sp && tsp && sidp && midp); TCULOG *ulog = ulrd->ulog; if(pthread_rwlock_rdlock(&ulog->rwlck) != 0) return NULL; if(ulrd->fd == -1){ char *path = tcsprintf("%s/%08d%s", ulog->base, ulrd->num, TCULSUFFIX); ulrd->fd = open(path, O_RDONLY, 00644); tcfree(path); if(ulrd->fd == -1){ pthread_rwlock_unlock(&ulog->rwlck); return NULL; } } int rsiz = sizeof(uint8_t) + sizeof(uint64_t) + sizeof(uint32_t) * 2; unsigned char buf[rsiz]; uint64_t ts; uint32_t sid, mid, size; while(true){ if(ulog->aiocbs && ulrd->num == ulog->max){ struct stat sbuf; if(fstat(ulrd->fd, &sbuf) == -1 || (sbuf.st_size < ulog->size && sbuf.st_size >= ulog->aioend)){ pthread_rwlock_unlock(&ulog->rwlck); return NULL; } } if(!tcread(ulrd->fd, buf, rsiz)){ if(ulrd->num < ulog->max){ close(ulrd->fd); ulrd->num++; char *path = tcsprintf("%s/%08d%s", ulog->base, ulrd->num, TCULSUFFIX); ulrd->fd = open(path, O_RDONLY, 00644); tcfree(path); if(ulrd->fd == -1){ pthread_rwlock_unlock(&ulog->rwlck); return NULL; } continue; } pthread_rwlock_unlock(&ulog->rwlck); return NULL; } const unsigned char *rp = buf; if(*rp != TCULMAGICNUM){ pthread_rwlock_unlock(&ulog->rwlck); return NULL; } rp += sizeof(uint8_t); memcpy(&ts, rp, sizeof(ts)); ts = TTNTOHLL(ts); rp += sizeof(ts); uint16_t snum; memcpy(&snum, rp, sizeof(snum)); sid = TTNTOHS(snum); rp += sizeof(snum); memcpy(&snum, rp, sizeof(snum)); mid = TTNTOHS(snum); rp += sizeof(snum); memcpy(&size, rp, sizeof(size)); size = TTNTOHL(size); rp += sizeof(size); if(ulrd->rsiz < size + 1){ ulrd->rbuf = tcrealloc(ulrd->rbuf, size + 1); ulrd->rsiz = size + 1; } if(!tcread(ulrd->fd, ulrd->rbuf, size)){ pthread_rwlock_unlock(&ulog->rwlck); return NULL; } if(ts < ulrd->ts) continue; break; } *sp = size; *tsp = ts; *sidp = sid; *midp = mid; ulrd->rbuf[size] = '\0'; pthread_rwlock_unlock(&ulog->rwlck); return ulrd->rbuf; } /* Store a record into an abstract database object. */ bool tculogadbput(TCULOG *ulog, uint32_t sid, uint32_t mid, TCADB *adb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){ assert(ulog && adb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0); bool err = false; int rmidx = tculogrmtxidx(ulog, kbuf, ksiz); bool dolog = tculogbegin(ulog, rmidx); if(!tcadbput(adb, kbuf, ksiz, vbuf, vsiz)) err = true; if(dolog){ unsigned char mstack[TTIOBUFSIZ]; int msiz = sizeof(uint8_t) * 3 + sizeof(uint32_t) * 2 + ksiz + vsiz; unsigned char *mbuf = (msiz < TTIOBUFSIZ) ? mstack : tcmalloc(msiz + 1); unsigned char *wp = mbuf; *(wp++) = TTMAGICNUM; *(wp++) = TTCMDPUT; uint32_t lnum; lnum = TTHTONL(ksiz); memcpy(wp, &lnum, sizeof(lnum)); wp += sizeof(lnum); lnum = TTHTONL(vsiz); memcpy(wp, &lnum, sizeof(lnum)); wp += sizeof(lnum); memcpy(wp, kbuf, ksiz); wp += ksiz; memcpy(wp, vbuf, vsiz); wp += vsiz; *(wp++) = err ? 1 : 0; if(!tculogwrite(ulog, 0, sid, mid, mbuf, msiz)) err = true; if(mbuf != mstack) tcfree(mbuf); tculogend(ulog, rmidx); } return !err; } /* Store a new record into an abstract database object. */ bool tculogadbputkeep(TCULOG *ulog, uint32_t sid, uint32_t mid, TCADB *adb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){ assert(ulog && adb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0); bool err = false; int rmidx = tculogrmtxidx(ulog, kbuf, ksiz); bool dolog = tculogbegin(ulog, rmidx); if(!tcadbputkeep(adb, kbuf, ksiz, vbuf, vsiz)) err = true; if(dolog){ unsigned char mstack[TTIOBUFSIZ]; int msiz = sizeof(uint8_t) * 3 + sizeof(uint32_t) * 2 + ksiz + vsiz; unsigned char *mbuf = (msiz < TTIOBUFSIZ) ? mstack : tcmalloc(msiz + 1); unsigned char *wp = mbuf; *(wp++) = TTMAGICNUM; *(wp++) = TTCMDPUTKEEP; uint32_t lnum; lnum = TTHTONL(ksiz); memcpy(wp, &lnum, sizeof(lnum)); wp += sizeof(lnum); lnum = TTHTONL(vsiz); memcpy(wp, &lnum, sizeof(lnum)); wp += sizeof(lnum); memcpy(wp, kbuf, ksiz); wp += ksiz; memcpy(wp, vbuf, vsiz); wp += vsiz; *(wp++) = err ? 1 : 0; if(!tculogwrite(ulog, 0, sid, mid, mbuf, msiz)) err = true; if(mbuf != mstack) tcfree(mbuf); tculogend(ulog, rmidx); } return !err; } /* Concatenate a value at the end of the existing record in an abstract database object. */ bool tculogadbputcat(TCULOG *ulog, uint32_t sid, uint32_t mid, TCADB *adb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){ assert(ulog && adb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0); bool err = false; int rmidx = tculogrmtxidx(ulog, kbuf, ksiz); bool dolog = tculogbegin(ulog, rmidx); if(!tcadbputcat(adb, kbuf, ksiz, vbuf, vsiz)) err = true; if(dolog){ unsigned char mstack[TTIOBUFSIZ]; int msiz = sizeof(uint8_t) * 3 + sizeof(uint32_t) * 2 + ksiz + vsiz; unsigned char *mbuf = (msiz < TTIOBUFSIZ) ? mstack : tcmalloc(msiz + 1); unsigned char *wp = mbuf; *(wp++) = TTMAGICNUM; *(wp++) = TTCMDPUTCAT; uint32_t lnum; lnum = TTHTONL(ksiz); memcpy(wp, &lnum, sizeof(lnum)); wp += sizeof(lnum); lnum = TTHTONL(vsiz); memcpy(wp, &lnum, sizeof(lnum)); wp += sizeof(lnum); memcpy(wp, kbuf, ksiz); wp += ksiz; memcpy(wp, vbuf, vsiz); wp += vsiz; *(wp++) = err ? 1 : 0; if(!tculogwrite(ulog, 0, sid, mid, mbuf, msiz)) err = true; if(mbuf != mstack) tcfree(mbuf); tculogend(ulog, rmidx); } return !err; } /* Concatenate a value at the end of the existing record and shift it to the left. */ bool tculogadbputshl(TCULOG *ulog, uint32_t sid, uint32_t mid, TCADB *adb, const void *kbuf, int ksiz, const void *vbuf, int vsiz, int width){ assert(ulog && adb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0 && width >= 0); bool err = false; int rmidx = tculogrmtxidx(ulog, kbuf, ksiz); bool dolog = tculogbegin(ulog, rmidx); PUTSHLOP op; op.vbuf = vbuf; op.vsiz = vsiz; op.width = width; if(!tcadbputproc(adb, kbuf, ksiz, vbuf, vsiz, (TCPDPROC)tculogadbputshlproc, &op)) err = true; if(dolog){ unsigned char mstack[TTIOBUFSIZ]; int msiz = sizeof(uint8_t) * 3 + sizeof(uint32_t) * 3 + ksiz + vsiz; unsigned char *mbuf = (msiz < TTIOBUFSIZ) ? mstack : tcmalloc(msiz + 1); unsigned char *wp = mbuf; *(wp++) = TTMAGICNUM; *(wp++) = TTCMDPUTSHL; uint32_t lnum; lnum = TTHTONL(ksiz); memcpy(wp, &lnum, sizeof(lnum)); wp += sizeof(lnum); lnum = TTHTONL(vsiz); memcpy(wp, &lnum, sizeof(lnum)); wp += sizeof(lnum); lnum = TTHTONL(width); memcpy(wp, &lnum, sizeof(lnum)); wp += sizeof(lnum); memcpy(wp, kbuf, ksiz); wp += ksiz; memcpy(wp, vbuf, vsiz); wp += vsiz; *(wp++) = err ? 1 : 0; if(!tculogwrite(ulog, 0, sid, mid, mbuf, msiz)) err = true; if(mbuf != mstack) tcfree(mbuf); tculogend(ulog, rmidx); } return !err; } /* Remove a record of an abstract database object. */ bool tculogadbout(TCULOG *ulog, uint32_t sid, uint32_t mid, TCADB *adb, const void *kbuf, int ksiz){ assert(ulog && adb && kbuf && ksiz >= 0); bool err = false; int rmidx = tculogrmtxidx(ulog, kbuf, ksiz); bool dolog = tculogbegin(ulog, rmidx); if(!tcadbout(adb, kbuf, ksiz)) err = true; if(dolog){ unsigned char mstack[TTIOBUFSIZ]; int msiz = sizeof(uint8_t) * 3 + sizeof(uint32_t) + ksiz; unsigned char *mbuf = (msiz < TTIOBUFSIZ) ? mstack : tcmalloc(msiz + 1); unsigned char *wp = mbuf; *(wp++) = TTMAGICNUM; *(wp++) = TTCMDOUT; uint32_t lnum; lnum = TTHTONL(ksiz); memcpy(wp, &lnum, sizeof(lnum)); wp += sizeof(lnum); memcpy(wp, kbuf, ksiz); wp += ksiz; *(wp++) = err ? 1 : 0; if(!tculogwrite(ulog, 0, sid, mid, mbuf, msiz)) err = true; if(mbuf != mstack) tcfree(mbuf); tculogend(ulog, rmidx); } return !err; } /* Add an integer to a record in an abstract database object. */ int tculogadbaddint(TCULOG *ulog, uint32_t sid, uint32_t mid, TCADB *adb, const void *kbuf, int ksiz, int num){ assert(ulog && adb && kbuf && ksiz >= 0); int rmidx = tculogrmtxidx(ulog, kbuf, ksiz); bool dolog = num != 0 && tculogbegin(ulog, rmidx); int rnum = tcadbaddint(adb, kbuf, ksiz, num); if(dolog){ unsigned char mstack[TTIOBUFSIZ]; int msiz = sizeof(uint8_t) * 3 + sizeof(uint32_t) * 2 + ksiz; unsigned char *mbuf = (msiz < TTIOBUFSIZ) ? mstack : tcmalloc(msiz + 1); unsigned char *wp = mbuf; *(wp++) = TTMAGICNUM; *(wp++) = TTCMDADDINT; uint32_t lnum; lnum = TTHTONL(ksiz); memcpy(wp, &lnum, sizeof(lnum)); wp += sizeof(lnum); lnum = TTHTONL(num); memcpy(wp, &lnum, sizeof(lnum)); wp += sizeof(lnum); memcpy(wp, kbuf, ksiz); wp += ksiz; *(wp++) = (rnum == INT_MIN) ? 1 : 0; if(!tculogwrite(ulog, 0, sid, mid, mbuf, msiz)) rnum = INT_MIN; if(mbuf != mstack) tcfree(mbuf); tculogend(ulog, rmidx); } return rnum; } /* Add a real number to a record in an abstract database object. */ double tculogadbadddouble(TCULOG *ulog, uint32_t sid, uint32_t mid, TCADB *adb, const void *kbuf, int ksiz, double num){ assert(ulog && adb && kbuf && ksiz >= 0); int rmidx = tculogrmtxidx(ulog, kbuf, ksiz); bool dolog = num != 0 && tculogbegin(ulog, rmidx); double rnum = tcadbadddouble(adb, kbuf, ksiz, num); if(dolog){ unsigned char mstack[TTIOBUFSIZ]; int msiz = sizeof(uint8_t) * 3 + sizeof(uint32_t) + sizeof(uint64_t) * 2 + ksiz; unsigned char *mbuf = (msiz < TTIOBUFSIZ) ? mstack : tcmalloc(msiz + 1); unsigned char *wp = mbuf; *(wp++) = TTMAGICNUM; *(wp++) = TTCMDADDDOUBLE; uint32_t lnum; lnum = TTHTONL(ksiz); memcpy(wp, &lnum, sizeof(lnum)); wp += sizeof(lnum); ttpackdouble(num, (char *)wp); wp += sizeof(uint64_t) * 2; memcpy(wp, kbuf, ksiz); wp += ksiz; *(wp++) = isnan(rnum) ? 1 : 0; if(!tculogwrite(ulog, 0, sid, mid, mbuf, msiz)) rnum = INT_MIN; if(mbuf != mstack) tcfree(mbuf); tculogend(ulog, rmidx); } return rnum; } /* Synchronize updated contents of an abstract database object with the file and the device. */ bool tculogadbsync(TCULOG *ulog, uint32_t sid, uint32_t mid, TCADB *adb){ assert(ulog && adb); bool err = false; bool dolog = tculogbegin(ulog, -1); if(!tcadbsync(adb)) err = true; if(dolog){ unsigned char mbuf[sizeof(uint8_t)*3]; unsigned char *wp = mbuf; *(wp++) = TTMAGICNUM; *(wp++) = TTCMDSYNC; *(wp++) = err ? 1 : 0; if(!tculogwrite(ulog, 0, sid, mid, mbuf, wp - mbuf)) err = true; tculogend(ulog, -1); } return !err; } /* Optimize the storage of an abstract database object. */ bool tculogadboptimize(TCULOG *ulog, uint32_t sid, uint32_t mid, TCADB *adb, const char *params){ assert(ulog && adb); bool err = false; bool dolog = tculogbegin(ulog, -1); if(!tcadboptimize(adb, params)) err = true; if(dolog){ int psiz = strlen(params); unsigned char mstack[TTIOBUFSIZ]; int msiz = sizeof(uint8_t) * 3 + sizeof(uint32_t) + psiz; unsigned char *mbuf = (msiz < TTIOBUFSIZ) ? mstack : tcmalloc(msiz + 1); unsigned char *wp = mbuf; *(wp++) = TTMAGICNUM; *(wp++) = TTCMDOPTIMIZE; uint32_t lnum; lnum = TTHTONL(psiz); memcpy(wp, &lnum, sizeof(lnum)); wp += sizeof(lnum); memcpy(wp, params, psiz); wp += psiz; *(wp++) = err ? 1 : 0; if(!tculogwrite(ulog, 0, sid, mid, mbuf, msiz)) err = true; if(mbuf != mstack) tcfree(mbuf); tculogend(ulog, -1); } return !err; } /* Remove all records of an abstract database object. */ bool tculogadbvanish(TCULOG *ulog, uint32_t sid, uint32_t mid, TCADB *adb){ assert(ulog && adb); bool err = false; bool dolog = tculogbegin(ulog, -1); if(!tcadbvanish(adb)) err = true; if(dolog){ unsigned char mbuf[sizeof(uint8_t)*3]; unsigned char *wp = mbuf; *(wp++) = TTMAGICNUM; *(wp++) = TTCMDVANISH; *(wp++) = err ? 1 : 0; if(!tculogwrite(ulog, 0, sid, mid, mbuf, wp - mbuf)) err = true; tculogend(ulog, -1); } return !err; } /* Call a versatile function for miscellaneous operations of an abstract database object. */ TCLIST *tculogadbmisc(TCULOG *ulog, uint32_t sid, uint32_t mid, TCADB *adb, const char *name, const TCLIST *args){ assert(ulog && adb && name && args); bool dolog = tculogbegin(ulog, -1); TCLIST *rv = tcadbmisc(adb, name, args); if(dolog){ int nsiz = strlen(name); int anum = tclistnum(args); int msiz = sizeof(uint8_t) * 3 + sizeof(uint32_t) * 2 + nsiz; for(int i = 0; i < anum; i++){ int esiz; tclistval(args, i, &esiz); msiz += sizeof(uint32_t) + esiz; } unsigned char mstack[TTIOBUFSIZ]; unsigned char *mbuf = (msiz < TTIOBUFSIZ) ? mstack : tcmalloc(msiz + 1); unsigned char *wp = mbuf; *(wp++) = TTMAGICNUM; *(wp++) = TTCMDMISC; uint32_t lnum; lnum = TTHTONL(nsiz); memcpy(wp, &lnum, sizeof(lnum)); wp += sizeof(lnum); lnum = TTHTONL(anum); memcpy(wp, &lnum, sizeof(lnum)); wp += sizeof(lnum); memcpy(wp, name, nsiz); wp += nsiz; for(int i = 0; i < anum; i++){ int esiz; const char *ebuf = tclistval(args, i, &esiz); lnum = TTHTONL(esiz); memcpy(wp, &lnum, sizeof(lnum)); wp += sizeof(lnum); memcpy(wp, ebuf, esiz); wp += esiz; } *(wp++) = rv ? 0 : 1; if(!tculogwrite(ulog, 0, sid, mid, mbuf, msiz)){ if(rv) tclistdel(rv); rv = NULL; } if(mbuf != mstack) tcfree(mbuf); tculogend(ulog, -1); } return rv; } /* Restore an abstract database object. */ bool tculogadbrestore(TCADB *adb, const char *path, uint64_t ts, bool con, TCULOG *ulog){ assert(adb && path); bool err = false; TCULOG *sulog = tculognew(); if(tculogopen(sulog, path, 0)){ TCULRD *ulrd = tculrdnew(sulog, ts); if(ulrd){ const char *rbuf; int rsiz; uint64_t rts; uint32_t rsid, rmid; while((rbuf = tculrdread(ulrd, &rsiz, &rts, &rsid, &rmid)) != NULL){ bool cc; if(!tculogadbredo(adb, rbuf, rsiz, ulog, rsid, rmid, &cc) || (con && !cc)){ err = true; break; } } tculrddel(ulrd); } else { err = true; } if(!tculogclose(sulog)) err = true; } else { err = true; } tculogdel(sulog); return !err; } /* Redo an update log message. */ bool tculogadbredo(TCADB *adb, const char *ptr, int size, TCULOG *ulog, uint32_t sid, uint32_t mid, bool *cp){ assert(adb && ptr && size >= 0); if(size < sizeof(uint8_t) * 3) return false; const unsigned char *rp = (unsigned char *)ptr; int magic = *(rp++); int cmd = *(rp++); bool exp = (((unsigned char *)ptr)[size-1] == 0) ? true : false; size -= sizeof(uint8_t) * 3; if(magic != TTMAGICNUM) return false; bool err = false; *cp = true; switch(cmd){ case TTCMDPUT: if(size >= sizeof(uint32_t) * 2){ uint32_t ksiz; memcpy(&ksiz, rp, sizeof(ksiz)); ksiz = TTNTOHL(ksiz); rp += sizeof(ksiz); uint32_t vsiz; memcpy(&vsiz, rp, sizeof(vsiz)); vsiz = TTNTOHL(vsiz); rp += sizeof(vsiz); if(tculogadbput(ulog, sid, mid, adb, rp, ksiz, rp + ksiz, vsiz) != exp) *cp = false; } else { err = true; } break; case TTCMDPUTKEEP: if(size >= sizeof(uint32_t) * 2){ uint32_t ksiz; memcpy(&ksiz, rp, sizeof(ksiz)); ksiz = TTNTOHL(ksiz); rp += sizeof(ksiz); uint32_t vsiz; memcpy(&vsiz, rp, sizeof(vsiz)); vsiz = TTNTOHL(vsiz); rp += sizeof(vsiz); if(tculogadbputkeep(ulog, sid, mid, adb, rp, ksiz, rp + ksiz, vsiz) != exp) *cp = false; } else { err = true; } break; case TTCMDPUTCAT: if(size >= sizeof(uint32_t) * 2){ uint32_t ksiz; memcpy(&ksiz, rp, sizeof(ksiz)); ksiz = TTNTOHL(ksiz); rp += sizeof(ksiz); uint32_t vsiz; memcpy(&vsiz, rp, sizeof(vsiz)); vsiz = TTNTOHL(vsiz); rp += sizeof(vsiz); if(tculogadbputcat(ulog, sid, mid, adb, rp, ksiz, rp + ksiz, vsiz) != exp) *cp = false; } else { err = true; } break; case TTCMDPUTSHL: if(size >= sizeof(uint32_t) * 3){ uint32_t ksiz; memcpy(&ksiz, rp, sizeof(ksiz)); ksiz = TTNTOHL(ksiz); rp += sizeof(ksiz); uint32_t vsiz; memcpy(&vsiz, rp, sizeof(vsiz)); vsiz = TTNTOHL(vsiz); rp += sizeof(vsiz); uint32_t width; memcpy(&width, rp, sizeof(width)); width = TTNTOHL(width); rp += sizeof(width); if(tculogadbputshl(ulog, sid, mid, adb, rp, ksiz, rp + ksiz, vsiz, width) != exp) *cp = false; } else { err = true; } break; case TTCMDOUT: if(size >= sizeof(uint32_t)){ uint32_t ksiz; memcpy(&ksiz, rp, sizeof(ksiz)); ksiz = TTNTOHL(ksiz); rp += sizeof(ksiz); if(tculogadbout(ulog, sid, mid, adb, rp, ksiz) != exp) *cp = false; } else { err = true; } break; case TTCMDADDINT: if(size >= sizeof(uint32_t) * 2){ uint32_t ksiz; memcpy(&ksiz, rp, sizeof(ksiz)); ksiz = TTNTOHL(ksiz); rp += sizeof(ksiz); int32_t num; memcpy(&num, rp, sizeof(num)); num = TTNTOHL(num); rp += sizeof(num); int rnum = tculogadbaddint(ulog, sid, mid, adb, rp, ksiz, num); if(exp && rnum == INT_MIN) *cp = false; } else { err = true; } break; case TTCMDADDDOUBLE: if(size >= sizeof(uint32_t) + sizeof(uint64_t) * 2){ uint32_t ksiz; memcpy(&ksiz, rp, sizeof(ksiz)); ksiz = TTNTOHL(ksiz); rp += sizeof(ksiz); double num = ttunpackdouble((char *)rp); rp += sizeof(uint64_t) * 2; double rnum = tculogadbadddouble(ulog, sid, mid, adb, rp, ksiz, num); if(exp && isnan(rnum)) *cp = false; } else { err = true; } break; case TTCMDSYNC: if(size == 0){ if(tculogadbsync(ulog, sid, mid, adb) != exp) *cp = false; } else { err = true; } break; case TTCMDOPTIMIZE: if(size >= sizeof(uint32_t)){ uint32_t psiz; memcpy(&psiz, rp, sizeof(psiz)); psiz = TTNTOHL(psiz); rp += sizeof(psiz); char *params = tcmemdup(rp, psiz); if(tculogadboptimize(ulog, sid, mid, adb, params) != exp) *cp = false; tcfree(params); } else { err = true; } break; case TTCMDVANISH: if(size == 0){ if(tculogadbvanish(ulog, sid, mid, adb) != exp) *cp = false; } else { err = true; } break; case TTCMDMISC: if(size >= sizeof(uint32_t) * 2){ uint32_t nsiz; memcpy(&nsiz, rp, sizeof(nsiz)); nsiz = TTNTOHL(nsiz); rp += sizeof(nsiz); uint32_t anum; memcpy(&anum, rp, sizeof(anum)); anum = TTNTOHL(anum); rp += sizeof(anum); char *name = tcmemdup(rp, nsiz); rp += nsiz; TCLIST *args = tclistnew2(anum); for(int i = 0; i < anum; i++){ uint32_t esiz; memcpy(&esiz, rp, sizeof(esiz)); esiz = TTNTOHL(esiz); rp += sizeof(esiz); tclistpush(args, rp, esiz); rp += esiz; } TCLIST *res = tculogadbmisc(ulog, sid, mid, adb, name, args); if(res){ if(!exp) *cp = false; tclistdel(res); } else { if(exp) *cp = false; } tclistdel(args); tcfree(name); } else { err = true; } break; default: err = true; break; } return !err; } /* Create a replication object. */ TCREPL *tcreplnew(void){ TCREPL *repl = tcmalloc(sizeof(*repl)); repl->fd = -1; repl->sock = NULL; return repl; } /* Delete a replication object. */ void tcrepldel(TCREPL *repl){ assert(repl); if(repl->fd >= 0) tcreplclose(repl); tcfree(repl); } /* Open a replication object. */ bool tcreplopen(TCREPL *repl, const char *host, int port, uint64_t ts, uint32_t sid){ assert(repl && host && port >= 0); if(repl->fd >= 0) return false; if(ts < 1) ts = 1; if(sid < 1) sid = INT_MAX; char addr[TTADDRBUFSIZ]; if(!ttgethostaddr(host, addr)) return false; int fd = ttopensock(addr, port); if(fd == -1) return false; unsigned char buf[TTIOBUFSIZ]; unsigned char *wp = buf; *(wp++) = TTMAGICNUM; *(wp++) = TTCMDREPL; uint64_t llnum = TTHTONLL(ts); memcpy(wp, &llnum, sizeof(llnum)); wp += sizeof(llnum); uint32_t lnum = TTHTONL(sid); memcpy(wp, &lnum, sizeof(lnum)); wp += sizeof(lnum); repl->fd = fd; repl->sock = ttsocknew(fd); repl->rbuf = tcmalloc(TTIOBUFSIZ); repl->rsiz = TTIOBUFSIZ; if(!ttsocksend(repl->sock, buf, wp - buf)){ tcreplclose(repl); return false; } repl->mid = ttsockgetint32(repl->sock); if(ttsockcheckend(repl->sock) || repl->mid < 1){ tcreplclose(repl); return false; } return true; } /* Close a remote database object. */ bool tcreplclose(TCREPL *repl){ assert(repl); if(repl->fd < 0) return false; bool err = false; tcfree(repl->rbuf); ttsockdel(repl->sock); if(!ttclosesock(repl->fd)) err = true; repl->fd = -1; repl->sock = NULL; return !err; } /* Read a message from a replication object. */ const char *tcreplread(TCREPL *repl, int *sp, uint64_t *tsp, uint32_t *sidp){ assert(repl && sp && tsp); int ocs = PTHREAD_CANCEL_DISABLE; pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &ocs); ttsocksetlife(repl->sock, TCREPLTIMEO); int c = ttsockgetc(repl->sock); if(c == TCULMAGICNOP){ *sp = 0; *tsp = 0; *sidp = 0; return ""; } if(c != TCULMAGICNUM){ pthread_setcancelstate(ocs, NULL); return NULL; } uint64_t ts = ttsockgetint64(repl->sock); uint32_t sid = ttsockgetint32(repl->sock); uint32_t rsiz = ttsockgetint32(repl->sock); if(repl->rsiz < rsiz + 1){ repl->rbuf = tcrealloc(repl->rbuf, rsiz + 1); repl->rsiz = rsiz + 1; } if(ttsockcheckend(repl->sock) || !ttsockrecv(repl->sock, repl->rbuf, rsiz) || ttsockcheckend(repl->sock)){ pthread_setcancelstate(ocs, NULL); return NULL; } *sp = rsiz; *tsp = ts; *sidp = sid; pthread_setcancelstate(ocs, NULL); return repl->rbuf; } /* Flush a AIO task. `aiocbp' specifies the pointer to the AIO task object. If successful, the return value is true, else, it is false. */ static bool tculogflushaiocbp(struct aiocb *aiocbp){ assert(aiocbp); if(!aiocbp->aio_buf) return true; bool err = false; while(true){ int rv = aio_error(aiocbp); if(rv == 0) break; if(rv != EINPROGRESS){ err = true; break; } if(aio_suspend((void *)&aiocbp, 1, NULL) == -1) err = true; } tcfree((char *)aiocbp->aio_buf); aiocbp->aio_buf = NULL; if(aio_return(aiocbp) != aiocbp->aio_nbytes) err = true; return !err; } /* Call back function for the putshl function. `vbuf' specifies the pointer to the region of the value. `vsiz' specifies the size of the region of the value. `sp' specifies the pointer to the variable into which the size of the region of the return value is assigned. `op' specifies the pointer to the optional opaque object. The return value is the pointer to the result object. */ static void *tculogadbputshlproc(const void *vbuf, int vsiz, int *sp, PUTSHLOP *op){ assert(vbuf && vsiz >= 0 && sp && op); int rsiz = tclmin(vsiz + op->vsiz, op->width); char *rbuf = tcmalloc(rsiz + 1); char *wp = rbuf; int wsiz = rsiz; int left = wsiz - op->vsiz; if(left > 0){ memcpy(wp, (char *)vbuf + vsiz - left, left); wp += left; wsiz -= left; } if(wsiz > 0) memcpy(wp, op->vbuf + op->vsiz - wsiz, wsiz); *sp = rsiz; return rbuf; } // END OF FILE tokyotyrant-1.1.40/tculog.h000066400000000000000000000433021133351147200156540ustar00rootroot00000000000000/************************************************************************************************* * The update log API of Tokyo Tyrant * Copyright (C) 2006-2010 Mikio Hirabayashi * This file is part of Tokyo Tyrant. * Tokyo Tyrant is free software; you can redistribute it and/or modify it under the terms of * the GNU Lesser General Public License as published by the Free Software Foundation; either * version 2.1 of the License or any later version. Tokyo Tyrant 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 Lesser General Public * License for more details. * You should have received a copy of the GNU Lesser General Public License along with Tokyo * Tyrant; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA. *************************************************************************************************/ #ifndef _TCULOG_H /* duplication check */ #define _TCULOG_H #if defined(__cplusplus) #define __TCULOG_CLINKAGEBEGIN extern "C" { #define __TCULOG_CLINKAGEEND } #else #define __TCULOG_CLINKAGEBEGIN #define __TCULOG_CLINKAGEEND #endif __TCULOG_CLINKAGEBEGIN #include /************************************************************************************************* * API *************************************************************************************************/ #define TCULSUFFIX ".ulog" /* suffix of update log files */ #define TCULMAGICNUM 0xc9 /* magic number of each command */ #define TCULMAGICNOP 0xca /* magic number of NOP command */ #define TCULRMTXNUM 31 /* number of mutexes of records */ typedef struct { /* type of structure for an update log */ pthread_mutex_t rmtxs[TCULRMTXNUM]; /* mutex for records */ pthread_rwlock_t rwlck; /* mutex for operation */ pthread_cond_t cnd; /* condition variable */ pthread_mutex_t wmtx; /* mutex for waiting condition */ char *base; /* path of the base directory */ uint64_t limsiz; /* limit size */ int max; /* number of maximum ID */ int fd; /* current file descriptor */ uint64_t size; /* current size */ void *aiocbs; /* AIO tasks */ int aiocbi; /* index of AIO tasks */ uint64_t aioend; /* end offset of AIO tasks */ } TCULOG; typedef struct { /* type of structure for a log reader */ TCULOG *ulog; /* update log object */ uint64_t ts; /* beginning timestamp */ int num; /* number of current ID */ int fd; /* current file descriptor */ char *rbuf; /* record buffer */ int rsiz; /* size of the record buffer */ } TCULRD; typedef struct { /* type of structure for a replication */ int fd; /* file descriptor */ TTSOCK *sock; /* socket object */ char *rbuf; /* record buffer */ int rsiz; /* size of the record buffer */ uint16_t mid; /* master server ID number */ } TCREPL; /* Create an update log object. The return value is the new update log object. */ TCULOG *tculognew(void); /* Delete an update log object. `ulog' specifies the update log object. */ void tculogdel(TCULOG *ulog); /* Set AIO control of an update log object. `ulog' specifies the update log object. If successful, the return value is true, else, it is false. */ bool tculogsetaio(TCULOG *ulog); /* Open files of an update log object. `ulog' specifies the update log object. `base' specifies the path of the base directory. `limsiz' specifies the limit size of each file. If it is not more than 0, no limit is specified. If successful, the return value is true, else, it is false. */ bool tculogopen(TCULOG *ulog, const char *base, uint64_t limsiz); /* Close files of an update log object. `ulog' specifies the update log object. If successful, the return value is true, else, it is false. */ bool tculogclose(TCULOG *ulog); /* Get the mutex index of a record. `ulog' specifies the update log object. `kbuf' specifies the pointer to the region of the key. `ksiz' specifies the size of the region of the key. The return value is the mutex index of a record. */ int tculogrmtxidx(TCULOG *ulog, const char *kbuf, int ksiz); /* Begin the critical section of an update log object. `ulog' specifies the update log object. `idx' specifies the index of the record lock. -1 means to lock all. If successful, the return value is true, else, it is false. */ bool tculogbegin(TCULOG *ulog, int idx); /* End the critical section of an update log object. `ulog' specifies the update log object. `idx' specifies the index of the record lock. -1 means to lock all. If successful, the return value is true, else, it is false. */ bool tculogend(TCULOG *ulog, int idx); /* Write a message into an update log object. `ulog' specifies the update log object. `ts' specifies the timestamp. If it is 0, the current time is specified. `sid' specifies the origin server ID of the message. `mid' specifies the master server ID of the message. `ptr' specifies the pointer to the region of the message. `size' specifies the size of the region. If successful, the return value is true, else, it is false. */ bool tculogwrite(TCULOG *ulog, uint64_t ts, uint32_t sid, uint32_t mid, const void *ptr, int size); /* Create a log reader object. `ulog' specifies the update log object. `ts' specifies the beginning timestamp. The return value is the new log reader object. */ TCULRD *tculrdnew(TCULOG *ulog, uint64_t ts); /* Delete a log reader object. `ulrd' specifies the log reader object. */ void tculrddel(TCULRD *ulrd); /* Wait the next message is written. `ulrd' specifies the log reader object. */ void tculrdwait(TCULRD *ulrd); /* Read a message from a log reader object. `ulrd' specifies the log reader object. `sp' specifies the pointer to the variable into which the size of the region of the return value is assigned. `tsp' specifies the pointer to the variable into which the timestamp of the next message is assigned. `sidp' specifies the pointer to the variable into which the origin server ID of the next message is assigned. `midp' specifies the pointer to the variable into which the master server ID of the next message is assigned. If successful, the return value is the pointer to the region of the value of the next message. `NULL' is returned if no record is to be read. */ const void *tculrdread(TCULRD *ulrd, int *sp, uint64_t *tsp, uint32_t *sidp, uint32_t *midp); /* Store a record into an abstract database object. `ulog' specifies the update log object. `sid' specifies the origin server ID of the message. `mid' specifies the master server ID of the message. `adb' specifies the abstract database object. `kbuf' specifies the pointer to the region of the key. `ksiz' specifies the size of the region of the key. `vbuf' specifies the pointer to the region of the value. `vsiz' specifies the size of the region of the value. If successful, the return value is true, else, it is false. If a record with the same key exists in the database, it is overwritten. */ bool tculogadbput(TCULOG *ulog, uint32_t sid, uint32_t mid, TCADB *adb, const void *kbuf, int ksiz, const void *vbuf, int vsiz); /* Store a new record into an abstract database object. `ulog' specifies the update log object. `sid' specifies the origin server ID of the message. `mid' specifies the master server ID of the message. `adb' specifies the abstract database object. `kbuf' specifies the pointer to the region of the key. `ksiz' specifies the size of the region of the key. `vbuf' specifies the pointer to the region of the value. `vsiz' specifies the size of the region of the value. If successful, the return value is true, else, it is false. If a record with the same key exists in the database, this function has no effect. */ bool tculogadbputkeep(TCULOG *ulog, uint32_t sid, uint32_t mid, TCADB *adb, const void *kbuf, int ksiz, const void *vbuf, int vsiz); /* Concatenate a value at the end of the existing record in an abstract database object. `ulog' specifies the update log object. `sid' specifies the origin server ID of the message. `mid' specifies the master server ID of the message. `adb' specifies the abstract database object. `kbuf' specifies the pointer to the region of the key. `ksiz' specifies the size of the region of the key. `vbuf' specifies the pointer to the region of the value. `vsiz' specifies the size of the region of the value. If successful, the return value is true, else, it is false. If there is no corresponding record, a new record is created. */ bool tculogadbputcat(TCULOG *ulog, uint32_t sid, uint32_t mid, TCADB *adb, const void *kbuf, int ksiz, const void *vbuf, int vsiz); /* Concatenate a value at the end of the existing record and shift it to the left. `ulog' specifies the update log object. `sid' specifies the origin server ID of the message. `mid' specifies the master server ID of the message. `adb' specifies the abstract database object. `kbuf' specifies the pointer to the region of the key. `ksiz' specifies the size of the region of the key. `vbuf' specifies the pointer to the region of the value. `vsiz' specifies the size of the region of the value. `width' specifies the width of the record. If successful, the return value is true, else, it is false. If there is no corresponding record, a new record is created. */ bool tculogadbputshl(TCULOG *ulog, uint32_t sid, uint32_t mid, TCADB *adb, const void *kbuf, int ksiz, const void *vbuf, int vsiz, int width); /* Remove a record of an abstract database object. `ulog' specifies the update log object. `sid' specifies the origin server ID of the message. `mid' specifies the master server ID of the message. `adb' specifies the abstract database object. `kbuf' specifies the pointer to the region of the key. `ksiz' specifies the size of the region of the key. If successful, the return value is true, else, it is false. */ bool tculogadbout(TCULOG *ulog, uint32_t sid, uint32_t mid, TCADB *adb, const void *kbuf, int ksiz); /* Add an integer to a record in an abstract database object. `ulog' specifies the update log object. `sid' specifies the origin server ID of the message. `mid' specifies the master server ID of the message. `adb' specifies the abstract database object connected as a writer. `kbuf' specifies the pointer to the region of the key. `ksiz' specifies the size of the region of the key. `num' specifies the additional value. If successful, the return value is the summation value, else, it is `INT_MIN'. If the corresponding record exists, the value is treated as an integer and is added to. If no record corresponds, a new record of the additional value is stored. */ int tculogadbaddint(TCULOG *ulog, uint32_t sid, uint32_t mid, TCADB *adb, const void *kbuf, int ksiz, int num); /* Add a real number to a record in an abstract database object. `ulog' specifies the update log object. `sid' specifies the origin server ID of the message. `mid' specifies the master server ID of the message. `adb' specifies the abstract database object connected as a writer. `kbuf' specifies the pointer to the region of the key. `ksiz' specifies the size of the region of the key. `num' specifies the additional value. If successful, the return value is the summation value, else, it is `NAN'. If the corresponding record exists, the value is treated as a real number and is added to. If no record corresponds, a new record of the additional value is stored. */ double tculogadbadddouble(TCULOG *ulog, uint32_t sid, uint32_t mid, TCADB *adb, const void *kbuf, int ksiz, double num); /* Synchronize updated contents of an abstract database object with the file and the device. `ulog' specifies the update log object. `sid' specifies the origin server ID of the message. `mid' specifies the master server ID of the message. `adb' specifies the abstract database object. If successful, the return value is true, else, it is false. */ bool tculogadbsync(TCULOG *ulog, uint32_t sid, uint32_t mid, TCADB *adb); /* Optimize the storage of an abstract database object. `ulog' specifies the update log object. `sid' specifies the origin server ID of the message. `mid' specifies the master server ID of the message. `adb' specifies the abstract database object. `params' specifies the string of the tuning parameters, which works as with the tuning of parameters the function `tcadbopen'. If it is `NULL', it is not used. If successful, the return value is true, else, it is false. */ bool tculogadboptimize(TCULOG *ulog, uint32_t sid, uint32_t mid, TCADB *adb, const char *params); /* Remove all records of an abstract database object. `ulog' specifies the update log object. `sid' specifies the origin server ID of the message. `mid' specifies the master server ID of the message. `adb' specifies the abstract database object. If successful, the return value is true, else, it is false. */ bool tculogadbvanish(TCULOG *ulog, uint32_t sid, uint32_t mid, TCADB *adb); /* Call a versatile function for miscellaneous operations of an abstract database object. `ulog' specifies the update log object. `sid' specifies the origin server ID of the message. `mid' specifies the master server ID of the message. `adb' specifies the abstract database object. `name' specifies the name of the function. `args' specifies a list object containing arguments. If successful, the return value is a list object of the result. `NULL' is returned on failure. All databases support "putlist", "outlist", and "getlist". "putdup" is to store records. It receives keys and values one after the other, and returns an empty list. "outlist" is to remove records. It receives keys, and returns an empty list. "getlist" is to retrieve records. It receives keys, and returns values. Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use. */ TCLIST *tculogadbmisc(TCULOG *ulog, uint32_t sid, uint32_t mid, TCADB *adb, const char *name, const TCLIST *args); /* Restore an abstract database object. `adb' specifies the abstract database object. `path' specifies the path of the update log directory. `ts' specifies the beginning time stamp. `con' specifies whether consistency checking is performed. `ulog' specifies the update log object. If successful, the return value is true, else, it is false. */ bool tculogadbrestore(TCADB *adb, const char *path, uint64_t ts, bool con, TCULOG *ulog); /* Redo an update log message. `adb' specifies the abstract database object. `ptr' specifies the pointer to the region of the message. `size' specifies the size of the region. `ulog' specifies the update log object. `sid' specifies the origin server ID of the message. `mid' specifies the master server ID of the message. `cp' specifies the pointer to the variable into which the result of consistency checking is assigned. If successful, the return value is true, else, it is false. */ bool tculogadbredo(TCADB *adb, const char *ptr, int size, TCULOG *ulog, uint32_t sid, uint32_t mid, bool *cp); /* Create a replication object. The return value is the new replicatoin object. */ TCREPL *tcreplnew(void); /* Delete a replication object. `repl' specifies the replication object. */ void tcrepldel(TCREPL *repl); /* Open a replication object. `repl' specifies the replication object. `host' specifies the name or the address of the server. `port' specifies the port number. `sid' specifies the server ID of self messages. If successful, the return value is true, else, it is false. */ bool tcreplopen(TCREPL *repl, const char *host, int port, uint64_t ts, uint32_t sid); /* Close a remote database object. `rdb' specifies the remote database object. If successful, the return value is true, else, it is false. */ bool tcreplclose(TCREPL *repl); /* Read a message from a replication object. `repl' specifies the replication object. `sp' specifies the pointer to the variable into which the size of the region of the return value is assigned. `tsp' specifies the pointer to the variable into which the timestamp of the next message is assigned. `sidp' specifies the pointer to the variable into which the origin server ID of the next message is assigned. If successful, the return value is the pointer to the region of the value of the next message. `NULL' is returned if no record is to be read. Empty string is returned when the no-operation command has been received. */ const char *tcreplread(TCREPL *repl, int *sp, uint64_t *tsp, uint32_t *sidp); __TCULOG_CLINKAGEEND #endif /* duplication check */ /* END OF FILE */ tokyotyrant-1.1.40/tokyotyrant.idl000066400000000000000000000122431133351147200173070ustar00rootroot00000000000000/************************************************************************************************* * IDL for bindings of scripting languages * Copyright (C) 2006-2009 Mikio Hirabayashi * This file is part of Tokyo Tyrant. * Tokyo Tyrant is free software; you can redistribute it and/or modify it under the terms of * the GNU Lesser General Public License as published by the Free Software Foundation; either * version 2.1 of the License or any later version. Tokyo Tyrant 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 Lesser General Public * License for more details. * You should have received a copy of the GNU Lesser General Public License along with Tokyo * Tyrant; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA. *************************************************************************************************/ /** * namespace of Tokyo Tyrant */ module TokyoTyrant { //---------------------------------------------------------------- // list of strings (substituted for by the native mechanism) //---------------------------------------------------------------- interface List { string get(in long index); }; //---------------------------------------------------------------- // map of strings (substituted for by the native mechanism) //---------------------------------------------------------------- interface Map { string get(in string key); }; //---------------------------------------------------------------- // the error codes //---------------------------------------------------------------- interface ECODE { const long ESUCCESS = 0; const long EINVALID = 1; const long ENOHOST = 2; const long EREFUSED = 3; const long ESEND = 4; const long ERECV = 5; const long EKEEP = 6; const long ENOREC = 7; const long EMISC = 9999; long ecode(); string errmsg(in long ecode); }; //---------------------------------------------------------------- // the remove database API //---------------------------------------------------------------- interface RDB :ECODE { const long XOLCKREC = 1 << 0; const long XOLCKGLB = 1 << 1; const long MONOULOG = 1 << 0; boolean open(in string host, in long port); boolean close(); boolean put(in string key, in string value); boolean putkeep(in string key, in string value); boolean putcat(in string key, in string value); boolean putshl(in string key, in string value, in long width); boolean putnr(in string key, in string value); boolean out(in string key); string get(in string key); long mget(inout Map recs); long vsiz(in string key); boolean iterinit(); string iternext(); List fwmkeys(in string prefix, in long max); long addint(in string key, in long num); double adddouble(in string key, in double num); string ext(in string name, in string key, in string value, in long opts); boolean sync(); boolean optimize(in string params); boolean vanish(); boolean copy(in string path); long long rnum(); long long size(); string stat(); List misc(in string name, in List args, in long opts); }; //---------------------------------------------------------------- // the table extension API //---------------------------------------------------------------- interface RDBTBL :RDB { const long ITLEXICAL = 0; const long ITDECIMAL = 1; const long ITTOKEN = 2; const long ITQGRAM = 3; const long ITOPT = 9998; const long ITVOID = 9999; const long ITKEEP = 1 << 24; boolean put(in string pkey, in Map cols); boolean putkeep(in string pkey, in Map cols); boolean putcat(in string pkey, in Map cols); boolean out(in string pkey); Map get(in string pkey); boolean setindex(in string name, in long type); long long genuid(); }; interface RDBQRY { const long QCSTREQ = 0; const long QCSTRINC = 1; const long QCSTRBW = 2; const long QCSTREW = 3; const long QCSTRAND = 4; const long QCSTROR = 5; const long QCSTROREQ = 6; const long QCSTRRX = 7; const long QCNUMEQ = 8; const long QCNUMGT = 9; const long QCNUMGE = 10; const long QCNUMLT = 11; const long QCNUMLE = 12; const long QCNUMBT = 13; const long QCNUMOREQ = 14; const long QCFTSPH = 15; const long QCFTSAND = 16; const long QCFTSOR = 17; const long QCFTSEX = 18; const long QCNEGATE = 1 << 24; const long QCNOIDX = 1 << 25; const long QOSTRASC = 0; const long QOSTRDESC = 1; const long QONUMASC = 2; const long QONUMDESC = 3; const long MSUNION = 0; const long MSISECT = 1; const long MSDIFF = 2; void addcond(in string name, in long op, in string expr); void setorder(in string name, in long type); void setlimit(in long max, in long skip); List search(); boolean searchout(); string hint(); typedef sequence QueryList; List metasearch(in QueryList qrys, in long type); }; }; /* END OF FILE */ tokyotyrant-1.1.40/tokyotyrant.pc.in000066400000000000000000000005061133351147200175450ustar00rootroot00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ datarootdir = @datarootdir@ bindir=@bindir@ libdir=@libdir@ libexecdir=@libexecdir@ includedir=@includedir@ datadir=@datadir@ Name: Tokyo Tyrant Description: network interface of Tokyo Cabinet Version: @PACKAGE_VERSION@ Libs: -L${libdir} -ltokyotyrant @LIBS@ Cflags: -I${includedir} tokyotyrant-1.1.40/ttservctl000077500000000000000000000060701133351147200161670ustar00rootroot00000000000000#! /bin/sh #---------------------------------------------------------------- # Startup script for the server of Tokyo Tyrant #---------------------------------------------------------------- # configuration variables prog="ttservctl" cmd="ttserver" basedir="/var/ttserver" port="1978" pidfile="$basedir/pid" #logfile="$basedir/log" #ulogdir="$basedir/ulog" #ulimsiz="256m" #sid=1 #mhost="remotehost1" #mport="1978" #rtsfile="$basedir/rts" dbname="$basedir/casket.tch#bnum=1000000" retval=0 # setting environment variables LANG=C LC_ALL=C PATH="$PATH:/sbin:/usr/sbin:/usr/local/sbin" export LANG LC_ALL PATH # start the server start(){ printf 'Starting the server of Tokyo Tyrant\n' mkdir -p "$basedir" if [ -z "$basedir" ] || [ -z "$port" ] || [ -z "$pidfile" ] || [ -z "$dbname" ] ; then printf 'Invalid configuration\n' retval=1 elif ! [ -d "$basedir" ] ; then printf 'No such directory: %s\n' "$basedir" retval=1 elif [ -f "$pidfile" ] ; then pid=`cat "$pidfile"` printf 'Existing process: %d\n' "$pid" retval=1 else cmd="$cmd -port $port -dmn -pid $pidfile" if [ -n "$logfile" ] ; then cmd="$cmd -log $logfile" fi if [ -n "$ulogdir" ] ; then mkdir -p "$ulogdir" cmd="$cmd -ulog $ulogdir" fi if [ -n "$ulimsiz" ] ; then cmd="$cmd -ulim $ulimsiz" fi if [ -n "$sid" ] ; then cmd="$cmd -sid $sid" fi if [ -n "$mhost" ] ; then cmd="$cmd -mhost $mhost" fi if [ -n "$mport" ] ; then cmd="$cmd -mport $mport" fi if [ -n "$rtsfile" ] ; then cmd="$cmd -rts $rtsfile" fi printf "Executing: %s\n" "$cmd" cmd="$cmd $dbname" $cmd if [ "$?" -eq 0 ] ; then printf 'Done\n' else printf 'The server could not started\n' retval=1 fi fi } # stop the server stop(){ printf 'Stopping the server of Tokyo Tyrant\n' if [ -f "$pidfile" ] ; then pid=`cat "$pidfile"` printf "Sending the terminal signal to the process: %s\n" "$pid" kill -TERM "$pid" c=0 while true ; do sleep 0.1 if [ -f "$pidfile" ] ; then c=`expr $c + 1` if [ "$c" -ge 100 ] ; then printf 'Hanging process: %d\n' "$pid" retval=1 break fi else printf 'Done\n' break fi done else printf 'No process found\n' retval=1 fi } # send HUP to the server for log rotation hup(){ printf 'Sending HUP signal to the server of Tokyo Tyrant\n' if [ -f "$pidfile" ] ; then pid=`cat "$pidfile"` printf "Sending the hangup signal to the process: %s\n" "$pid" kill -HUP "$pid" printf 'Done\n' else printf 'No process found\n' retval=1 fi } # check permission if [ -d "$basedir" ] && ! touch "$basedir/$$" >/dev/null 2>&1 then printf 'Permission denied\n' exit 1 fi rm -f "$basedir/$$" # dispatch the command case "$1" in start) start ;; stop) stop ;; restart) stop start ;; hup) hup ;; *) printf 'Usage: %s {start|stop|restart|hup}\n' "$prog" exit 1 ;; esac # exit exit "$retval" # END OF FILE tokyotyrant-1.1.40/ttserver.c000066400000000000000000004040131133351147200162300ustar00rootroot00000000000000/************************************************************************************************* * The server of Tokyo Tyrant * Copyright (C) 2006-2010 Mikio Hirabayashi * This file is part of Tokyo Tyrant. * Tokyo Tyrant is free software; you can redistribute it and/or modify it under the terms of * the GNU Lesser General Public License as published by the Free Software Foundation; either * version 2.1 of the License or any later version. Tokyo Tyrant 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 Lesser General Public * License for more details. * You should have received a copy of the GNU Lesser General Public License along with Tokyo * Tyrant; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA. *************************************************************************************************/ #include #include #include #include "myconf.h" #include "scrext.h" #define DEFTHNUM 8 // default thread number #define DEFPIDPATH "ttserver.pid" // default name of the PID file #define DEFRTSPATH "ttserver.rts" // default name of the RTS file #define DEFULIMSIZ (1LL<<30) // default limit size of an update log file #define MAXARGSIZ (256<<20) // maximum size of each argument #define MAXARGNUM (1<<20) // maximum number of arguments #define NUMBUFSIZ 32 // size of a numeric buffer #define LINEBUFSIZ 8192 // size of a line buffer #define TOKENUNIT 256 // unit number of tokens #define RECMTXNUM 31 // number of mutexes of records #define STASHBNUM 1021 // bucket number of the script stash object #define REPLPERIOD 1.0 // period of calling replication request enum { // enumeration for command sequential numbers TTSEQPUT, // sequential number of put command TTSEQPUTKEEP, // sequential number of putkeep command TTSEQPUTCAT, // sequential number of putcat command TTSEQPUTSHL, // sequential number of putshl command TTSEQPUTNR, // sequential number of putnr command TTSEQOUT, // sequential number of out command TTSEQGET, // sequential number of get command TTSEQMGET, // sequential number of mget command TTSEQVSIZ, // sequential number of vsiz command TTSEQITERINIT, // sequential number of iterinit command TTSEQITERNEXT, // sequential number of iternext command TTSEQFWMKEYS, // sequential number of fwmkeys command TTSEQADDINT, // sequential number of addint command TTSEQADDDOUBLE, // sequential number of adddouble command TTSEQEXT, // sequential number of ext command TTSEQSYNC, // sequential number of sync command TTSEQOPTIMIZE, // sequential number of sync command TTSEQVANISH, // sequential number of vanish command TTSEQCOPY, // sequential number of copy command TTSEQRESTORE, // sequential number of restore command TTSEQSETMST, // sequential number of setmst command TTSEQRNUM, // sequential number of rnum command TTSEQSIZE, // sequential number of size command TTSEQSTAT, // sequential number of stat command TTSEQMISC, // sequential number of stat command TTSEQREPL, // sequential number of repl command TTSEQSLAVE, // sequential number of slave command TTSEQALLORG, // sequential number of all commands the original TTSEQALLMC, // sequential number of all commands the memcached TTSEQALLHTTP, // sequential number of all commands the HTTP TTSEQALLREAD, // sequential number of all commands of reading TTSEQALLWRITE, // sequential number of all commands of writing TTSEQALLMANAGE // sequential number of all commands of managing }; enum { // enumeration for command sequential numbers TTSEQPUTMISS = TTSEQSLAVE, // sequential number of misses of get commands TTSEQOUTMISS, // sequential number of misses of out commands TTSEQGETMISS, // sequential number of misses of get commands TTSEQNUM // number of sequential numbers }; typedef struct { // type of structure of logging opaque object int fd; } LOGARG; typedef struct { // type of structure of master synchronous object char host[TTADDRBUFSIZ]; // host name int port; // port number const char *rtspath; // path of the replication time stamp file uint64_t rts; // replication time stamp int opts; // options TCADB *adb; // database object TCULOG *ulog; // update log object uint32_t sid; // server ID number bool fail; // failure flag bool recon; // re-connect flag bool fatal; // fatal error flag uint64_t mts; // modified time stamp } REPLARG; typedef struct { // type of structure of periodic opaque object const char *name; // function name TCADB *adb; // database object TCULOG *ulog; // update log object uint32_t sid; // server ID number REPLARG *sarg; // replication object void *scrext; // script extension object } EXTPCARG; typedef struct { // type of structure of task opaque object int thnum; // number of threads uint64_t *counts; // conunters of execution uint64_t mask; // bit mask of commands TCADB *adb; // database object TCULOG *ulog; // update log object uint32_t sid; // server ID number REPLARG *sarg; // replication object pthread_mutex_t rmtxs[RECMTXNUM]; // mutex for records void **screxts; // script extension objects } TASKARG; typedef struct { // type of structure of termination opaque object int thnum; // number of threads TCADB *adb; // database object REPLARG *sarg; // replication object void **screxts; // script extension objects EXTPCARG *pcargs; // periodic opaque objects int pcnum; // number of periodic opaque objects bool err; // error flag } TERMARG; /* global variables */ const char *g_progname = NULL; // program name double g_starttime = 0.0; // start time TTSERV *g_serv = NULL; // server object int g_loglevel = TTLOGINFO; // whether to log debug information bool g_restart = false; // restart flag /* function prototypes */ int main(int argc, char **argv); static void usage(void); static uint64_t getcmdmask(const char *expr); static void sigtermhandler(int signum); static void sigchldhandler(int signum); static int proc(const char *dbname, const char *host, int port, int thnum, int tout, bool dmn, const char *pidpath, bool kl, const char *logpath, const char *ulogpath, uint64_t ulim, bool uas, uint32_t sid, const char *mhost, int mport, const char *rtspath, int ropts, const char *skelpath, int mulnum, const char *extpath, const TCLIST *extpcs, uint64_t mask); static void do_log(int level, const char *msg, void *opq); static void do_slave(void *opq); static void do_extpc(void *opq); static void do_task(TTSOCK *sock, void *opq, TTREQ *req); static char **tokenize(char *str, int *np); static uint32_t recmtxidx(const char *kbuf, int ksiz); static uint64_t sumstat(TASKARG *arg, int seq); static void do_put(TTSOCK *sock, TASKARG *arg, TTREQ *req); static void do_putkeep(TTSOCK *sock, TASKARG *arg, TTREQ *req); static void do_putcat(TTSOCK *sock, TASKARG *arg, TTREQ *req); static void do_putshl(TTSOCK *sock, TASKARG *arg, TTREQ *req); static void do_putnr(TTSOCK *sock, TASKARG *arg, TTREQ *req); static void do_out(TTSOCK *sock, TASKARG *arg, TTREQ *req); static void do_get(TTSOCK *sock, TASKARG *arg, TTREQ *req); static void do_mget(TTSOCK *sock, TASKARG *arg, TTREQ *req); static void do_vsiz(TTSOCK *sock, TASKARG *arg, TTREQ *req); static void do_iterinit(TTSOCK *sock, TASKARG *arg, TTREQ *req); static void do_iternext(TTSOCK *sock, TASKARG *arg, TTREQ *req); static void do_fwmkeys(TTSOCK *sock, TASKARG *arg, TTREQ *req); static void do_addint(TTSOCK *sock, TASKARG *arg, TTREQ *req); static void do_adddouble(TTSOCK *sock, TASKARG *arg, TTREQ *req); static void do_ext(TTSOCK *sock, TASKARG *arg, TTREQ *req); static void do_sync(TTSOCK *sock, TASKARG *arg, TTREQ *req); static void do_optimize(TTSOCK *sock, TASKARG *arg, TTREQ *req); static void do_vanish(TTSOCK *sock, TASKARG *arg, TTREQ *req); static void do_copy(TTSOCK *sock, TASKARG *arg, TTREQ *req); static void do_restore(TTSOCK *sock, TASKARG *arg, TTREQ *req); static void do_setmst(TTSOCK *sock, TASKARG *arg, TTREQ *req); static void do_rnum(TTSOCK *sock, TASKARG *arg, TTREQ *req); static void do_size(TTSOCK *sock, TASKARG *arg, TTREQ *req); static void do_stat(TTSOCK *sock, TASKARG *arg, TTREQ *req); static void do_misc(TTSOCK *sock, TASKARG *arg, TTREQ *req); static void do_repl(TTSOCK *sock, TASKARG *arg, TTREQ *req); static void do_mc_set(TTSOCK *sock, TASKARG *arg, TTREQ *req, char **tokens, int tnum); static void do_mc_add(TTSOCK *sock, TASKARG *arg, TTREQ *req, char **tokens, int tnum); static void do_mc_replace(TTSOCK *sock, TASKARG *arg, TTREQ *req, char **tokens, int tnum); static void do_mc_append(TTSOCK *sock, TASKARG *arg, TTREQ *req, char **tokens, int tnum); static void do_mc_prepend(TTSOCK *sock, TASKARG *arg, TTREQ *req, char **tokens, int tnum); static void do_mc_get(TTSOCK *sock, TASKARG *arg, TTREQ *req, char **tokens, int tnum); static void do_mc_delete(TTSOCK *sock, TASKARG *arg, TTREQ *req, char **tokens, int tnum); static void do_mc_incr(TTSOCK *sock, TASKARG *arg, TTREQ *req, char **tokens, int tnum); static void do_mc_decr(TTSOCK *sock, TASKARG *arg, TTREQ *req, char **tokens, int tnum); static void do_mc_stats(TTSOCK *sock, TASKARG *arg, TTREQ *req, char **tokens, int tnum); static void do_mc_flushall(TTSOCK *sock, TASKARG *arg, TTREQ *req, char **tokens, int tnum); static void do_mc_version(TTSOCK *sock, TASKARG *arg, TTREQ *req, char **tokens, int tnum); static void do_mc_quit(TTSOCK *sock, TASKARG *arg, TTREQ *req, char **tokens, int tnum); static void do_http_get(TTSOCK *sock, TASKARG *arg, TTREQ *req, int ver, const char *uri); static void do_http_head(TTSOCK *sock, TASKARG *arg, TTREQ *req, int ver, const char *uri); static void do_http_put(TTSOCK *sock, TASKARG *arg, TTREQ *req, int ver, const char *uri); static void do_http_post(TTSOCK *sock, TASKARG *arg, TTREQ *req, int ver, const char *uri); static void do_http_delete(TTSOCK *sock, TASKARG *arg, TTREQ *req, int ver, const char *uri); static void do_http_options(TTSOCK *sock, TASKARG *arg, TTREQ *req, int ver, const char *uri); static void do_term(void *opq); /* main routine */ int main(int argc, char **argv){ g_progname = argv[0]; g_starttime = tctime(); char *dbname = NULL; char *host = NULL; char *pidpath = NULL; char *logpath = NULL; char *ulogpath = NULL; char *mhost = NULL; char *rtspath = NULL; char *skelpath = NULL; char *extpath = NULL; TCLIST *extpcs = NULL; int port = TTDEFPORT; int thnum = DEFTHNUM; int tout = 0; bool dmn = false; bool kl = false; uint64_t ulim = DEFULIMSIZ; bool uas = false; uint32_t sid = 0; int mport = TTDEFPORT; int ropts = 0; int mulnum = 0; uint64_t mask = 0; for(int i = 1; i < argc; i++){ if(!dbname && argv[i][0] == '-'){ if(!strcmp(argv[i], "-host")){ if(++i >= argc) usage(); host = argv[i]; } else if(!strcmp(argv[i], "-port")){ if(++i >= argc) usage(); port = tcatoi(argv[i]); } else if(!strcmp(argv[i], "-thnum")){ if(++i >= argc) usage(); thnum = tcatoi(argv[i]); } else if(!strcmp(argv[i], "-tout")){ if(++i >= argc) usage(); tout = tcatoi(argv[i]); } else if(!strcmp(argv[i], "-dmn")){ dmn = true; } else if(!strcmp(argv[i], "-pid")){ if(++i >= argc) usage(); pidpath = argv[i]; } else if(!strcmp(argv[i], "-kl")){ kl = true; } else if(!strcmp(argv[i], "-log")){ if(++i >= argc) usage(); logpath = argv[i]; } else if(!strcmp(argv[i], "-ld")){ g_loglevel = TTLOGDEBUG; } else if(!strcmp(argv[i], "-le")){ g_loglevel = TTLOGERROR; } else if(!strcmp(argv[i], "-ulog")){ if(++i >= argc) usage(); ulogpath = argv[i]; } else if(!strcmp(argv[i], "-ulim")){ if(++i >= argc) usage(); ulim = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-uas")){ uas = true; } else if(!strcmp(argv[i], "-sid")){ if(++i >= argc) usage(); sid = tcatoi(argv[i]); } else if(!strcmp(argv[i], "-mhost")){ if(++i >= argc) usage(); mhost = argv[i]; } else if(!strcmp(argv[i], "-mport")){ if(++i >= argc) usage(); mport = tcatoi(argv[i]); } else if(!strcmp(argv[i], "-rts")){ if(++i >= argc) usage(); rtspath = argv[i]; } else if(!strcmp(argv[i], "-rcc")){ ropts |= RDBROCHKCON; } else if(!strcmp(argv[i], "-skel")){ if(++i >= argc) usage(); skelpath = argv[i]; } else if(!strcmp(argv[i], "-mul")){ if(++i >= argc) usage(); mulnum = tcatoi(argv[i]); } else if(!strcmp(argv[i], "-ext")){ if(++i >= argc) usage(); extpath = argv[i]; } else if(!strcmp(argv[i], "-extpc")){ if(!extpcs) extpcs = tclistnew2(1); if(++i >= argc) usage(); tclistpush2(extpcs, argv[i]); if(++i >= argc) usage(); tclistpush2(extpcs, argv[i]); } else if(!strcmp(argv[i], "-mask")){ if(++i >= argc) usage(); mask |= getcmdmask(argv[i]); } else if(!strcmp(argv[i], "-unmask")){ if(++i >= argc) usage(); mask &= ~getcmdmask(argv[i]); } else if(!strcmp(argv[i], "--version")){ printf("Tokyo Tyrant version %s (%d:%s) for %s\n", ttversion, _TT_LIBVER, _TT_PROTVER, TTSYSNAME); printf("Copyright (C) 2006-2010 Mikio Hirabayashi\n"); exit(0); } else { usage(); } } else if(!dbname){ dbname = argv[i]; } else { usage(); } } if(!dbname) dbname = "*"; if(thnum < 1 || mport < 1) usage(); if(dmn && !pidpath) pidpath = DEFPIDPATH; if(!rtspath) rtspath = DEFRTSPATH; g_serv = ttservnew(); int rv = proc(dbname, host, port, thnum, tout, dmn, pidpath, kl, logpath, ulogpath, ulim, uas, sid, mhost, mport, rtspath, ropts, skelpath, mulnum, extpath, extpcs, mask); ttservdel(g_serv); if(extpcs) tclistdel(extpcs); return rv; } /* print the usage and exit */ static void usage(void){ fprintf(stderr, "%s: the server of Tokyo Tyrant\n", g_progname); fprintf(stderr, "\n"); fprintf(stderr, "usage:\n"); fprintf(stderr, " %s [-host name] [-port num] [-thnum num] [-tout num]" " [-dmn] [-pid path] [-kl] [-log path] [-ld|-le] [-ulog path] [-ulim num] [-uas]" " [-sid num] [-mhost name] [-mport num] [-rts path] [-rcc] [-skel name] [-mul num]" " [-ext path] [-extpc name period] [-mask expr] [-unmask expr] [dbname]\n", g_progname); fprintf(stderr, "\n"); exit(1); } /* get the bit mask of a command name */ static uint64_t getcmdmask(const char *expr){ uint64_t mask = 0; TCLIST *fields = tcstrsplit(expr, " ,"); for(int i = 0; i < tclistnum(fields); i++){ const char *name = tclistval2(fields, i); if(tcstrifwm(name, "0x")){ mask |= tcatoih(name); } else if(!tcstricmp(name, "put")){ mask |= 1ULL << TTSEQPUT; } else if(!tcstricmp(name, "putkeep")){ mask |= 1ULL << TTSEQPUTKEEP; } else if(!tcstricmp(name, "putcat")){ mask |= 1ULL << TTSEQPUTCAT; } else if(!tcstricmp(name, "putshl")){ mask |= 1ULL << TTSEQPUTSHL; } else if(!tcstricmp(name, "putnr")){ mask |= 1ULL << TTSEQPUTNR; } else if(!tcstricmp(name, "out")){ mask |= 1ULL << TTSEQOUT; } else if(!tcstricmp(name, "get")){ mask |= 1ULL << TTSEQGET; } else if(!tcstricmp(name, "mget")){ mask |= 1ULL << TTSEQMGET; } else if(!tcstricmp(name, "vsiz")){ mask |= 1ULL << TTSEQVSIZ; } else if(!tcstricmp(name, "iterinit")){ mask |= 1ULL << TTSEQITERINIT; } else if(!tcstricmp(name, "iternext")){ mask |= 1ULL << TTSEQITERNEXT; } else if(!tcstricmp(name, "fwmkeys")){ mask |= 1ULL << TTSEQFWMKEYS; } else if(!tcstricmp(name, "addint")){ mask |= 1ULL << TTSEQADDINT; } else if(!tcstricmp(name, "adddouble")){ mask |= 1ULL << TTSEQADDDOUBLE; } else if(!tcstricmp(name, "ext")){ mask |= 1ULL << TTSEQEXT; } else if(!tcstricmp(name, "sync")){ mask |= 1ULL << TTSEQSYNC; } else if(!tcstricmp(name, "optimize")){ mask |= 1ULL << TTSEQOPTIMIZE; } else if(!tcstricmp(name, "vanish")){ mask |= 1ULL << TTSEQVANISH; } else if(!tcstricmp(name, "copy")){ mask |= 1ULL << TTSEQCOPY; } else if(!tcstricmp(name, "restore")){ mask |= 1ULL << TTSEQRESTORE; } else if(!tcstricmp(name, "setmst")){ mask |= 1ULL << TTSEQSETMST; } else if(!tcstricmp(name, "rnum")){ mask |= 1ULL << TTSEQRNUM; } else if(!tcstricmp(name, "size")){ mask |= 1ULL << TTSEQSIZE; } else if(!tcstricmp(name, "stat")){ mask |= 1ULL << TTSEQSTAT; } else if(!tcstricmp(name, "misc")){ mask |= 1ULL << TTSEQMISC; } else if(!tcstricmp(name, "repl")){ mask |= 1ULL << TTSEQREPL; } else if(!tcstricmp(name, "slave")){ mask |= 1ULL << TTSEQSLAVE; } else if(!tcstricmp(name, "all")){ mask |= UINT64_MAX; } else if(!tcstricmp(name, "allorg")){ mask |= 1ULL << TTSEQALLORG; } else if(!tcstricmp(name, "allmc")){ mask |= 1ULL << TTSEQALLMC; } else if(!tcstricmp(name, "allhttp")){ mask |= 1ULL << TTSEQALLHTTP; } else if(!tcstricmp(name, "allread")){ mask |= 1ULL << TTSEQALLREAD; } else if(!tcstricmp(name, "allwrite")){ mask |= 1ULL << TTSEQALLWRITE; } else if(!tcstricmp(name, "allmanage")){ mask |= 1ULL << TTSEQALLMANAGE; } } tclistdel(fields); return mask; } /* handle termination signals */ static void sigtermhandler(int signum){ if(signum == SIGHUP) g_restart = true; ttservkill(g_serv); } /* handle child event signals */ static void sigchldhandler(int signum){ return; } /* perform the command */ static int proc(const char *dbname, const char *host, int port, int thnum, int tout, bool dmn, const char *pidpath, bool kl, const char *logpath, const char *ulogpath, uint64_t ulim, bool uas, uint32_t sid, const char *mhost, int mport, const char *rtspath, int ropts, const char *skelpath, int mulnum, const char *extpath, const TCLIST *extpcs, uint64_t mask){ LOGARG larg; larg.fd = 1; ttservsetloghandler(g_serv, do_log, &larg); if(dmn){ if(dbname && *dbname != '*' && *dbname != '+' && *dbname != MYPATHCHR) ttservlog(g_serv, TTLOGINFO, "warning: dbname(%s) is not the absolute path", dbname); if(port == 0 && host && *host != MYPATHCHR) ttservlog(g_serv, TTLOGINFO, "warning: host(%s) is not the absolute path", host); if(pidpath && *pidpath != MYPATHCHR) ttservlog(g_serv, TTLOGINFO, "warning: pid(%s) is not the absolute path", pidpath); if(logpath && *logpath != MYPATHCHR) ttservlog(g_serv, TTLOGINFO, "warning: log(%s) is not the absolute path", logpath); if(ulogpath && *ulogpath != MYPATHCHR) ttservlog(g_serv, TTLOGINFO, "warning: ulog(%s) is not the absolute path", ulogpath); if(mport == 0 && mhost && *mhost != MYPATHCHR) ttservlog(g_serv, TTLOGINFO, "warning: mhost(%s) is not the absolute path", mhost); if(mhost && rtspath && *rtspath != MYPATHCHR) ttservlog(g_serv, TTLOGINFO, "warning: rts(%s) is not the absolute path", rtspath); if(skelpath && strchr(skelpath, MYPATHCHR) && *skelpath != MYPATHCHR) ttservlog(g_serv, TTLOGINFO, "warning: skel(%s) is not the absolute path", skelpath); if(extpath && *extpath != MYPATHCHR) ttservlog(g_serv, TTLOGINFO, "warning: ext(%s) is not the absolute path", extpath); if(chdir("/") == -1){ ttservlog(g_serv, TTLOGERROR, "chdir failed"); return 1; } } if(!skelpath && dbname && *dbname != '*' && *dbname != '+' && !strstr(dbname, ".tc")) ttservlog(g_serv, TTLOGINFO, "warning: dbname(%s) has no suffix for database type", dbname); struct stat sbuf; if(ulogpath && (stat(ulogpath, &sbuf) != 0 || !S_ISDIR(sbuf.st_mode))) ttservlog(g_serv, TTLOGINFO, "warning: ulog(%s) is not a directory", ulogpath); if(pidpath){ char *numstr = tcreadfile(pidpath, -1, NULL); if(numstr && kl){ int64_t pid = tcatoi(numstr); tcfree(numstr); ttservlog(g_serv, TTLOGINFO, "warning: killing the process %lld with SIGTERM", (long long)pid); if(kill(pid, SIGTERM) != 0) ttservlog(g_serv, TTLOGERROR, "kill failed"); int cnt = 0; while(true){ tcsleep(0.1); if((numstr = tcreadfile(pidpath, -1, NULL)) != NULL){ tcfree(numstr); } else { break; } if(++cnt >= 100){ ttservlog(g_serv, TTLOGINFO, "warning: killing the process %lld with SIGKILL", (long long)pid); if(kill(pid, SIGKILL) != 0) ttservlog(g_serv, TTLOGERROR, "kill failed"); unlink(pidpath); break; } } numstr = tcreadfile(pidpath, -1, NULL); } if(numstr){ int64_t pid = tcatoi(numstr); tcfree(numstr); ttservlog(g_serv, TTLOGERROR, "the process %lld may be already running", (long long)pid); return 1; } } if(sid > UINT16_MAX){ ttservlog(g_serv, TTLOGINFO, "warning: the SID is ignored because it exceeds %d", UINT16_MAX); sid = 0; } if(sid < 1){ if(ulogpath){ ttservlog(g_serv, TTLOGINFO, "warning: update logging is omitted because the SID is not specified"); ulogpath = NULL; } if(mhost){ ttservlog(g_serv, TTLOGINFO, "warning: replication is omitted because the SID is not specified"); mhost = NULL; } } if(dmn && !ttdaemonize()){ ttservlog(g_serv, TTLOGERROR, "ttdaemonize failed"); return 1; } if(logpath){ int fd = open(logpath, O_WRONLY | O_APPEND | O_CREAT, 00644); if(fd != -1){ larg.fd = fd; } else { ttservlog(g_serv, TTLOGERROR, "the log file %s could not be opened", logpath); return 1; } } int64_t pid = getpid(); ttservlog(g_serv, TTLOGSYSTEM, "--------- logging started [%lld] --------", (long long)pid); if(pidpath){ char buf[32]; sprintf(buf, "%lld\n", (long long)pid); if(!tcwritefile(pidpath, buf, strlen(buf))){ ttservlog(g_serv, TTLOGERROR, "tcwritefile failed"); return 1; } ttservlog(g_serv, TTLOGSYSTEM, "process ID configuration: path=%s pid=%lld", pidpath, (long long)pid); } ttservlog(g_serv, TTLOGSYSTEM, "server configuration: host=%s port=%d", host ? host : "(any)", port); if(!ttservconf(g_serv, host, port)) return 1; struct rlimit rlbuf; memset(&rlbuf, 0, sizeof(rlbuf)); if(getrlimit(RLIMIT_NOFILE, &rlbuf) == 0 && rlbuf.rlim_cur != RLIM_INFINITY){ rlim_t min = rlbuf.rlim_cur; for(rlim_t max = INT32_MAX; max > min; max /= 2){ rlbuf.rlim_cur = max; rlbuf.rlim_max = max; if(setrlimit(RLIMIT_NOFILE, &rlbuf) == 0) break; } } else { ttservlog(g_serv, TTLOGERROR, "getrlimit failed"); } memset(&rlbuf, 0, sizeof(rlbuf)); if(getrlimit(RLIMIT_NOFILE, &rlbuf) == 0){ ttservlog(g_serv, TTLOGSYSTEM, "maximum connection: %d", (int)rlbuf.rlim_cur); } else { ttservlog(g_serv, TTLOGERROR, "getrlimit failed"); } bool err = false; ADBSKEL skel; memset(&skel, 0, sizeof(skel)); void *skellib = NULL; if(skelpath){ ttservlog(g_serv, TTLOGSYSTEM, "skeleton database library: %s", skelpath); skellib = dlopen(skelpath, RTLD_LAZY); if(!skellib){ err = true; ttservlog(g_serv, TTLOGERROR, "dlopen failed: %s", dlerror()); } } TCADB *adb = tcadbnew(); if(skellib){ void *initsym = dlsym(skellib, "initialize"); if(initsym){ bool (*initfunc)(ADBSKEL *); memcpy(&initfunc, &initsym, sizeof(initsym)); if(initfunc(&skel)){ if(!tcadbsetskel(adb, &skel)){ if(skel.opq && skel.del) skel.del(skel.opq); err = true; ttservlog(g_serv, TTLOGERROR, "tcadbsetskel failed"); } } else { if(skel.opq && skel.del) skel.del(skel.opq); err = true; ttservlog(g_serv, TTLOGERROR, "initialize failed"); } } else { err = true; ttservlog(g_serv, TTLOGERROR, "dlsym failed: %s", dlerror()); } } ttservlog(g_serv, TTLOGSYSTEM, "opening the database: %s", dbname); if(mulnum > 0 && !tcadbsetskelmulti(adb, mulnum)){ err = true; ttservlog(g_serv, TTLOGERROR, "tcadbsetskelmulti failed"); } if(!tcadbopen(adb, dbname)){ err = true; ttservlog(g_serv, TTLOGERROR, "tcadbopen failed"); } TCULOG *ulog = tculognew(); if(ulogpath){ ttservlog(g_serv, TTLOGSYSTEM, "update log configuration: path=%s limit=%llu async=%d sid=%d", ulogpath, (unsigned long long)ulim, uas, sid); if(uas && !tculogsetaio(ulog)){ err = true; ttservlog(g_serv, TTLOGERROR, "tculogsetaio failed"); } if(!tculogopen(ulog, ulogpath, ulim)){ err = true; ttservlog(g_serv, TTLOGERROR, "tculogopen failed"); } } ttservtune(g_serv, thnum, tout); if(mhost) ttservlog(g_serv, TTLOGSYSTEM, "replication configuration: host=%s port=%d ropts=%d", mhost, mport, ropts); uint64_t *counts = tccalloc(sizeof(*counts), (TTSEQNUM) * thnum); void *screxts[thnum]; TCMDB *scrstash = NULL; TCMDB *scrlock = NULL; pthread_mutex_t *scrlcks = NULL; if(extpath){ ttservlog(g_serv, TTLOGSYSTEM, "scripting extension: %s", extpath); scrstash = tcmdbnew2(STASHBNUM); scrlock = tcmdbnew2(thnum * 2 + 1); bool screrr = false; for(int i = 0; i < thnum; i++){ screxts[i] = NULL; } for(int i = 0; i < thnum; i++){ screxts[i] = scrextnew(screxts, thnum, i, extpath, adb, ulog, sid, scrstash, scrlock, do_log, &larg); if(!screxts[i]) screrr = true; } if(screrr){ err = true; ttservlog(g_serv, TTLOGERROR, "scrextnew failed"); } } else { for(int i = 0; i < thnum; i++){ screxts[i] = NULL; } } if(mask != 0) ttservlog(g_serv, TTLOGSYSTEM, "command bit mask: 0x%llx", (unsigned long long)mask); REPLARG sarg; snprintf(sarg.host, TTADDRBUFSIZ, "%s", mhost ? mhost : ""); sarg.port = mport; sarg.rtspath = rtspath; sarg.rts = 0; sarg.opts = ropts; sarg.adb = adb; sarg.ulog = ulog; sarg.sid = sid; sarg.fail = false; sarg.recon = false; sarg.fatal = false; sarg.mts = 0; if(!(mask & (1ULL << TTSEQSLAVE))) ttservaddtimedhandler(g_serv, REPLPERIOD, do_slave, &sarg); EXTPCARG *pcargs = NULL; int pcnum = 0; if(extpath && extpcs){ pcnum = tclistnum(extpcs) / 2; pcargs = tcmalloc(sizeof(*pcargs) * pcnum); for(int i = 0; i < pcnum; i++){ const char *name = tclistval2(extpcs, i * 2); double period = tcatof(tclistval2(extpcs, i * 2 + 1)); EXTPCARG *pcarg = pcargs + i; pcarg->name = name; pcarg->adb = adb; pcarg->ulog = ulog; pcarg->sid = sid; pcarg->sarg = &sarg; pcarg->scrext = scrextnew(screxts, thnum, thnum + i, extpath, adb, ulog, sid, scrstash, scrlock, do_log, &larg); if(pcarg->scrext){ if(*name && period > 0) ttservaddtimedhandler(g_serv, period, do_extpc, pcarg); } else { err = true; ttservlog(g_serv, TTLOGERROR, "scrextnew failed"); } } } TASKARG targ; targ.thnum = thnum; targ.counts = counts; targ.mask = mask; targ.adb = adb; targ.ulog = ulog; targ.sid = sid; targ.sarg = &sarg; for(int i = 0; i < RECMTXNUM; i++){ if(pthread_mutex_init(targ.rmtxs + i, NULL) != 0) ttservlog(g_serv, TTLOGERROR, "pthread_mutex_init failed"); } targ.screxts = screxts; ttservsettaskhandler(g_serv, do_task, &targ); TERMARG karg; karg.thnum = thnum; karg.adb = adb; karg.sarg = &sarg; karg.screxts = screxts; karg.pcargs = pcargs; karg.pcnum = pcnum; karg.err = false; ttservsettermhandler(g_serv, do_term, &karg); if(larg.fd != 1){ close(larg.fd); larg.fd = 1; } do { g_restart = false; if(logpath){ int fd = open(logpath, O_WRONLY | O_APPEND | O_CREAT, 00644); if(fd != -1){ larg.fd = fd; } else { err = true; ttservlog(g_serv, TTLOGERROR, "open failed"); } } if(signal(SIGTERM, sigtermhandler) == SIG_ERR || signal(SIGINT, sigtermhandler) == SIG_ERR || signal(SIGHUP, sigtermhandler) == SIG_ERR || signal(SIGPIPE, SIG_IGN) == SIG_ERR || signal(SIGCHLD, sigchldhandler) == SIG_ERR){ err = true; ttservlog(g_serv, TTLOGERROR, "signal failed"); } if(!ttservstart(g_serv)) err = true; } while(g_restart); if(karg.err) err = true; if(pcargs){ for(int i = 0; i < pcnum; i++){ EXTPCARG *pcarg = pcargs + i; if(!pcarg->scrext) continue; if(!scrextdel(pcarg->scrext)){ err = true; ttservlog(g_serv, TTLOGERROR, "scrextdel failed"); } } tcfree(pcargs); } for(int i = 0; i < RECMTXNUM; i++){ if(pthread_mutex_destroy(targ.rmtxs + i) != 0) ttservlog(g_serv, TTLOGERROR, "pthread_mutex_destroy failed"); } for(int i = 0; i < thnum; i++){ if(!screxts[i]) continue; if(!scrextdel(screxts[i])){ err = true; ttservlog(g_serv, TTLOGERROR, "scrextdel failed"); } } if(scrlcks){ for(int i = 0; i < RECMTXNUM; i++){ if(pthread_mutex_destroy(scrlcks + i) != 0) ttservlog(g_serv, TTLOGERROR, "pthread_mutex_destroy failed"); } tcfree(scrlcks); } if(scrlock) tcmdbdel(scrlock); if(scrstash) tcmdbdel(scrstash); tcfree(counts); if(ulogpath && !tculogclose(ulog)){ err = true; ttservlog(g_serv, TTLOGERROR, "tculogclose failed"); } tculogdel(ulog); tcadbdel(adb); if(skellib && dlclose(skellib) != 0){ err = true; ttservlog(g_serv, TTLOGERROR, "dlclose failed"); } if(pidpath && unlink(pidpath) != 0){ err = true; ttservlog(g_serv, TTLOGERROR, "unlink failed"); } ttservlog(g_serv, TTLOGSYSTEM, "--------- logging finished [%d] --------", pid); if(logpath && close(larg.fd) == -1) err = true; return err ? 1 : 0; } /* handle a log message */ static void do_log(int level, const char *msg, void *opq){ if(level < g_loglevel) return; LOGARG *arg = (LOGARG *)opq; char date[48]; tcdatestrwww(INT64_MAX, INT_MAX, date); const char *lvstr = "unknown"; switch(level){ case TTLOGDEBUG: lvstr = "DEBUG"; break; case TTLOGINFO: lvstr = "INFO"; break; case TTLOGERROR: lvstr = "ERROR"; break; case TTLOGSYSTEM: lvstr = "SYSTEM"; break; } char buf[LINEBUFSIZ]; int len = snprintf(buf, LINEBUFSIZ, "%s\t%s\t%s\n", date, lvstr, msg); if(len >= LINEBUFSIZ){ buf[LINEBUFSIZ-1] = '\n'; len = LINEBUFSIZ; } tcwrite(arg ? arg->fd : 1, buf, len); } /* replicate master data */ static void do_slave(void *opq){ REPLARG *arg = opq; TCADB *adb = arg->adb; TCULOG *ulog = arg->ulog; uint32_t sid = arg->sid; if(arg->fatal) return; if(arg->host[0] == '\0' || arg->port < 1) return; if(arg->mts > 0){ char rtsbuf[NUMBUFSIZ]; int len = sprintf(rtsbuf, "%llu\n", (unsigned long long)arg->mts); if(!tcwritefile(arg->rtspath, rtsbuf, len)) ttservlog(g_serv, TTLOGERROR, "do_slave: tcwritefile failed"); arg->mts = 0; } int rtsfd = open(arg->rtspath, O_RDWR | O_CREAT, 00644); if(rtsfd == -1){ ttservlog(g_serv, TTLOGERROR, "do_slave: open failed"); return; } struct stat sbuf; if(fstat(rtsfd, &sbuf) == -1){ ttservlog(g_serv, TTLOGERROR, "do_slave: stat failed"); close(rtsfd); return; } char rtsbuf[NUMBUFSIZ]; memset(rtsbuf, 0, NUMBUFSIZ); arg->rts = 0; if(sbuf.st_size > 0 && tcread(rtsfd, rtsbuf, tclmin(NUMBUFSIZ - 1, sbuf.st_size))) arg->rts = tcatoi(rtsbuf); TCREPL *repl = tcreplnew(); pthread_cleanup_push((void (*)(void *))tcrepldel, repl); if(tcreplopen(repl, arg->host, arg->port, arg->rts + 1, sid)){ ttservlog(g_serv, TTLOGINFO, "replicating from sid=%u (%s:%d) after %llu", repl->mid, arg->host, arg->port, (unsigned long long)arg->rts); arg->fail = false; arg->recon = false; bool err = false; uint32_t rsid; const char *rbuf; int rsiz; uint64_t rts; while(!err && !ttserviskilled(g_serv) && !arg->recon && (rbuf = tcreplread(repl, &rsiz, &rts, &rsid)) != NULL){ if(rsiz < 1) continue; bool cc; if(!tculogadbredo(adb, rbuf, rsiz, ulog, rsid, repl->mid, &cc)){ err = true; ttservlog(g_serv, TTLOGERROR, "do_slave: tculogadbredo failed"); } else if(!cc){ if(arg->opts & RDBROCHKCON){ err = true; arg->fatal = true; ttservlog(g_serv, TTLOGERROR, "do_slave: detected inconsistency"); } else { ttservlog(g_serv, TTLOGINFO, "do_slave: detected inconsistency"); } } if(lseek(rtsfd, 0, SEEK_SET) != -1){ int len = sprintf(rtsbuf, "%llu\n", (unsigned long long)rts); if(tcwrite(rtsfd, rtsbuf, len)){ arg->rts = rts; } else { err = true; ttservlog(g_serv, TTLOGERROR, "do_slave: tcwrite failed"); } } else { err = true; ttservlog(g_serv, TTLOGERROR, "do_slave: lseek failed"); } } tcreplclose(repl); ttservlog(g_serv, TTLOGINFO, "replication finished"); } else { if(!arg->fail) ttservlog(g_serv, TTLOGERROR, "do_slave: tcreplopen failed"); arg->fail = true; } pthread_cleanup_pop(1); if(close(rtsfd) == -1) ttservlog(g_serv, TTLOGERROR, "do_slave: close failed"); } /* perform an extension command */ static void do_extpc(void *opq){ EXTPCARG *arg = (EXTPCARG *)opq; const char *name = arg->name; void *scr = arg->scrext; int xsiz; char *xbuf = scrextcallmethod(scr, name, "", 0, "", 0, &xsiz); tcfree(xbuf); } /* handle a task and dispatch it */ static void do_task(TTSOCK *sock, void *opq, TTREQ *req){ TASKARG *arg = (TASKARG *)opq; int c = ttsockgetc(sock); if(c == TTMAGICNUM){ switch(ttsockgetc(sock)){ case TTCMDPUT: do_put(sock, arg, req); break; case TTCMDPUTKEEP: do_putkeep(sock, arg, req); break; case TTCMDPUTCAT: do_putcat(sock, arg, req); break; case TTCMDPUTSHL: do_putshl(sock, arg, req); break; case TTCMDPUTNR: do_putnr(sock, arg, req); break; case TTCMDOUT: do_out(sock, arg, req); break; case TTCMDGET: do_get(sock, arg, req); break; case TTCMDMGET: do_mget(sock, arg, req); break; case TTCMDVSIZ: do_vsiz(sock, arg, req); break; case TTCMDITERINIT: do_iterinit(sock, arg, req); break; case TTCMDITERNEXT: do_iternext(sock, arg, req); break; case TTCMDFWMKEYS: do_fwmkeys(sock, arg, req); break; case TTCMDADDINT: do_addint(sock, arg, req); break; case TTCMDADDDOUBLE: do_adddouble(sock, arg, req); break; case TTCMDEXT: do_ext(sock, arg, req); break; case TTCMDSYNC: do_sync(sock, arg, req); break; case TTCMDOPTIMIZE: do_optimize(sock, arg, req); break; case TTCMDVANISH: do_vanish(sock, arg, req); break; case TTCMDCOPY: do_copy(sock, arg, req); break; case TTCMDRESTORE: do_restore(sock, arg, req); break; case TTCMDSETMST: do_setmst(sock, arg, req); break; case TTCMDRNUM: do_rnum(sock, arg, req); break; case TTCMDSIZE: do_size(sock, arg, req); break; case TTCMDSTAT: do_stat(sock, arg, req); break; case TTCMDMISC: do_misc(sock, arg, req); break; case TTCMDREPL: do_repl(sock, arg, req); break; default: ttservlog(g_serv, TTLOGINFO, "unknown command"); break; } } else { ttsockungetc(sock, c); char *line = ttsockgets2(sock); if(line){ pthread_cleanup_push(tcfree, line); int tnum; char **tokens = tokenize(line, &tnum); pthread_cleanup_push(tcfree, tokens); if(tnum > 0){ const char *cmd = tokens[0]; if(!strcmp(cmd, "set")){ do_mc_set(sock, arg, req, tokens, tnum); } else if(!strcmp(cmd, "add")){ do_mc_add(sock, arg, req, tokens, tnum); } else if(!strcmp(cmd, "replace")){ do_mc_replace(sock, arg, req, tokens, tnum); } else if(!strcmp(cmd, "append")){ do_mc_append(sock, arg, req, tokens, tnum); } else if(!strcmp(cmd, "prepend")){ do_mc_prepend(sock, arg, req, tokens, tnum); } else if(!strcmp(cmd, "get") || !strcmp(cmd, "gets")){ do_mc_get(sock, arg, req, tokens, tnum); } else if(!strcmp(cmd, "delete")){ do_mc_delete(sock, arg, req, tokens, tnum); } else if(!strcmp(cmd, "incr")){ do_mc_incr(sock, arg, req, tokens, tnum); } else if(!strcmp(cmd, "decr")){ do_mc_decr(sock, arg, req, tokens, tnum); } else if(!strcmp(cmd, "stats")){ do_mc_stats(sock, arg, req, tokens, tnum); } else if(!strcmp(cmd, "flush_all")){ do_mc_flushall(sock, arg, req, tokens, tnum); } else if(!strcmp(cmd, "version")){ do_mc_version(sock, arg, req, tokens, tnum); } else if(!strcmp(cmd, "quit")){ do_mc_quit(sock, arg, req, tokens, tnum); } else if(tnum > 2 && tcstrfwm(tokens[2], "HTTP/1.")){ int ver = tcatoi(tokens[2] + 7); const char *uri = tokens[1]; if(tcstrifwm(uri, "http://")){ const char *pv = strchr(uri + 7, '/'); if(pv) uri = pv; } if(!strcmp(cmd, "GET")){ do_http_get(sock, arg, req, ver, uri); } else if(!strcmp(cmd, "HEAD")){ do_http_head(sock, arg, req, ver, uri); } else if(!strcmp(cmd, "PUT")){ do_http_put(sock, arg, req, ver, uri); } else if(!strcmp(cmd, "POST")){ do_http_post(sock, arg, req, ver, uri); } else if(!strcmp(cmd, "DELETE")){ do_http_delete(sock, arg, req, ver, uri); } else if(!strcmp(cmd, "OPTIONS")){ do_http_options(sock, arg, req, ver, uri); } } } pthread_cleanup_pop(1); pthread_cleanup_pop(1); } } } /* tokenize a string */ static char **tokenize(char *str, int *np){ int anum = TOKENUNIT; char **tokens = tcmalloc(sizeof(*tokens) * anum); int tnum = 0; while(*str == ' ' || *str == '\t'){ str++; } while(*str != '\0'){ if(tnum >= anum){ anum *= 2; tokens = tcrealloc(tokens, sizeof(*tokens) * anum); } tokens[tnum++] = str; while(*str != '\0' && *str != ' ' && *str != '\t'){ str++; } while(*str == ' ' || *str == '\t'){ *(str++) = '\0'; } } *np = tnum; return tokens; } /* get the mutex index of a record */ static uint32_t recmtxidx(const char *kbuf, int ksiz){ uint32_t hash = 725; while(ksiz--){ hash = hash * 29 + *(uint8_t *)kbuf++; } return hash % RECMTXNUM; } /* get the summation of status information of a command */ static uint64_t sumstat(TASKARG *arg, int seq){ int thnum = arg->thnum; uint64_t *counts = arg->counts; uint64_t sum = 0; for(int i = 0; i < thnum; i++){ sum += counts[TTSEQNUM*i+seq]; } return sum; } /* handle the put command */ static void do_put(TTSOCK *sock, TASKARG *arg, TTREQ *req){ ttservlog(g_serv, TTLOGDEBUG, "doing put command"); arg->counts[TTSEQNUM*req->idx+TTSEQPUT]++; uint64_t mask = arg->mask; TCADB *adb = arg->adb; TCULOG *ulog = arg->ulog; uint32_t sid = arg->sid; int ksiz = ttsockgetint32(sock); int vsiz = ttsockgetint32(sock); if(ttsockcheckend(sock) || ksiz < 0 || ksiz > MAXARGSIZ || vsiz < 0 || vsiz > MAXARGSIZ){ ttservlog(g_serv, TTLOGINFO, "do_put: invalid parameters"); return; } int rsiz = ksiz + vsiz; char stack[TTIOBUFSIZ]; char *buf = (rsiz < TTIOBUFSIZ) ? stack : tcmalloc(rsiz + 1); pthread_cleanup_push(free, (buf == stack) ? NULL : buf); if(ttsockrecv(sock, buf, rsiz) && !ttsockcheckend(sock)){ uint8_t code = 0; if(mask & ((1ULL << TTSEQPUT) | (1ULL << TTSEQALLORG) | (1ULL << TTSEQALLWRITE))){ code = 1; ttservlog(g_serv, TTLOGINFO, "do_put: forbidden"); } else if(!tculogadbput(ulog, sid, 0, adb, buf, ksiz, buf + ksiz, vsiz)){ arg->counts[TTSEQNUM*req->idx+TTSEQPUTMISS]++; code = 1; ttservlog(g_serv, TTLOGERROR, "do_put: operation failed"); } if(ttsocksend(sock, &code, sizeof(code))){ req->keep = true; } else { ttservlog(g_serv, TTLOGINFO, "do_put: response failed"); } } else { ttservlog(g_serv, TTLOGINFO, "do_put: invalid entity"); } pthread_cleanup_pop(1); } /* handle the putkeep command */ static void do_putkeep(TTSOCK *sock, TASKARG *arg, TTREQ *req){ ttservlog(g_serv, TTLOGDEBUG, "doing putkeep command"); arg->counts[TTSEQNUM*req->idx+TTSEQPUTKEEP]++; uint64_t mask = arg->mask; TCADB *adb = arg->adb; TCULOG *ulog = arg->ulog; uint32_t sid = arg->sid; int ksiz = ttsockgetint32(sock); int vsiz = ttsockgetint32(sock); if(ttsockcheckend(sock) || ksiz < 0 || ksiz > MAXARGSIZ || vsiz < 0 || vsiz > MAXARGSIZ){ ttservlog(g_serv, TTLOGINFO, "do_putkeep: invalid parameters"); return; } int rsiz = ksiz + vsiz; char stack[TTIOBUFSIZ]; char *buf = (rsiz < TTIOBUFSIZ) ? stack : tcmalloc(rsiz + 1); pthread_cleanup_push(free, (buf == stack) ? NULL : buf); if(ttsockrecv(sock, buf, rsiz) && !ttsockcheckend(sock)){ uint8_t code = 0; if(mask & ((1ULL << TTSEQPUTKEEP) | (1ULL << TTSEQALLORG) | (1ULL << TTSEQALLWRITE))){ code = 1; ttservlog(g_serv, TTLOGINFO, "do_putkeep: forbidden"); } else if(!tculogadbputkeep(ulog, sid, 0, adb, buf, ksiz, buf + ksiz, vsiz)){ arg->counts[TTSEQNUM*req->idx+TTSEQPUTMISS]++; code = 1; } if(ttsocksend(sock, &code, sizeof(code))){ req->keep = true; } else { ttservlog(g_serv, TTLOGINFO, "do_putkeep: response failed"); } } else { ttservlog(g_serv, TTLOGINFO, "do_putkeep: invalid entity"); } pthread_cleanup_pop(1); } /* handle the putcat command */ static void do_putcat(TTSOCK *sock, TASKARG *arg, TTREQ *req){ ttservlog(g_serv, TTLOGDEBUG, "doing putcat command"); arg->counts[TTSEQNUM*req->idx+TTSEQPUTCAT]++; uint64_t mask = arg->mask; TCADB *adb = arg->adb; TCULOG *ulog = arg->ulog; uint32_t sid = arg->sid; int ksiz = ttsockgetint32(sock); int vsiz = ttsockgetint32(sock); if(ttsockcheckend(sock) || ksiz < 0 || ksiz > MAXARGSIZ || vsiz < 0 || vsiz > MAXARGSIZ){ ttservlog(g_serv, TTLOGINFO, "do_putcat: invalid parameters"); return; } int rsiz = ksiz + vsiz; char stack[TTIOBUFSIZ]; char *buf = (rsiz < TTIOBUFSIZ) ? stack : tcmalloc(rsiz + 1); pthread_cleanup_push(free, (buf == stack) ? NULL : buf); if(ttsockrecv(sock, buf, rsiz) && !ttsockcheckend(sock)){ uint8_t code = 0; if(mask & ((1ULL << TTSEQPUTCAT) | (1ULL << TTSEQALLORG) | (1ULL << TTSEQALLWRITE))){ code = 1; ttservlog(g_serv, TTLOGINFO, "do_putcat: forbidden"); } else if(!tculogadbputcat(ulog, sid, 0, adb, buf, ksiz, buf + ksiz, vsiz)){ arg->counts[TTSEQNUM*req->idx+TTSEQPUTMISS]++; code = 1; ttservlog(g_serv, TTLOGERROR, "do_putcat: operation failed"); } if(ttsocksend(sock, &code, sizeof(code))){ req->keep = true; } else { ttservlog(g_serv, TTLOGINFO, "do_putcat: response failed"); } } else { ttservlog(g_serv, TTLOGINFO, "do_putcat: invalid entity"); } pthread_cleanup_pop(1); } /* handle the putshl command */ static void do_putshl(TTSOCK *sock, TASKARG *arg, TTREQ *req){ ttservlog(g_serv, TTLOGDEBUG, "doing putshl command"); arg->counts[TTSEQNUM*req->idx+TTSEQPUTSHL]++; uint64_t mask = arg->mask; TCADB *adb = arg->adb; TCULOG *ulog = arg->ulog; uint32_t sid = arg->sid; int ksiz = ttsockgetint32(sock); int vsiz = ttsockgetint32(sock); int width = ttsockgetint32(sock); if(ttsockcheckend(sock) || ksiz < 0 || ksiz > MAXARGSIZ || vsiz < 0 || vsiz > MAXARGSIZ || width < 0){ ttservlog(g_serv, TTLOGINFO, "do_putshl: invalid parameters"); return; } int rsiz = ksiz + vsiz; char stack[TTIOBUFSIZ]; char *buf = (rsiz < TTIOBUFSIZ) ? stack : tcmalloc(rsiz + 1); pthread_cleanup_push(free, (buf == stack) ? NULL : buf); if(ttsockrecv(sock, buf, rsiz) && !ttsockcheckend(sock)){ uint8_t code = 0; if(mask & ((1ULL << TTSEQPUTSHL) | (1ULL << TTSEQALLORG) | (1ULL << TTSEQALLWRITE))){ code = 1; ttservlog(g_serv, TTLOGINFO, "do_putshl: forbidden"); } else if(!tculogadbputshl(ulog, sid, 0, adb, buf, ksiz, buf + ksiz, vsiz, width)){ arg->counts[TTSEQNUM*req->idx+TTSEQPUTMISS]++; code = 1; ttservlog(g_serv, TTLOGERROR, "do_putshl: operation failed"); } if(ttsocksend(sock, &code, sizeof(code))){ req->keep = true; } else { ttservlog(g_serv, TTLOGINFO, "do_putshl: response failed"); } } else { ttservlog(g_serv, TTLOGINFO, "do_putshl: invalid entity"); } pthread_cleanup_pop(1); } /* handle the putnr command */ static void do_putnr(TTSOCK *sock, TASKARG *arg, TTREQ *req){ ttservlog(g_serv, TTLOGDEBUG, "doing putnr command"); arg->counts[TTSEQNUM*req->idx+TTSEQPUTNR]++; uint64_t mask = arg->mask; TCADB *adb = arg->adb; TCULOG *ulog = arg->ulog; uint32_t sid = arg->sid; int ksiz = ttsockgetint32(sock); int vsiz = ttsockgetint32(sock); if(ttsockcheckend(sock) || ksiz < 0 || ksiz > MAXARGSIZ || vsiz < 0 || vsiz > MAXARGSIZ){ ttservlog(g_serv, TTLOGINFO, "do_putnr: invalid parameters"); return; } int rsiz = ksiz + vsiz; char stack[TTIOBUFSIZ]; char *buf = (rsiz < TTIOBUFSIZ) ? stack : tcmalloc(rsiz + 1); pthread_cleanup_push(free, (buf == stack) ? NULL : buf); if(ttsockrecv(sock, buf, rsiz) && !ttsockcheckend(sock)){ uint8_t code = 0; if(mask & ((1ULL << TTSEQPUTNR) | (1ULL << TTSEQALLORG) | (1ULL << TTSEQALLWRITE))){ code = 1; ttservlog(g_serv, TTLOGINFO, "do_putnr: forbidden"); } else if(!tculogadbput(ulog, sid, 0, adb, buf, ksiz, buf + ksiz, vsiz)){ arg->counts[TTSEQNUM*req->idx+TTSEQPUTMISS]++; code = 1; ttservlog(g_serv, TTLOGERROR, "do_putnr: operation failed"); } req->keep = true; } else { ttservlog(g_serv, TTLOGINFO, "do_putnr: invalid entity"); } pthread_cleanup_pop(1); } /* handle the out command */ static void do_out(TTSOCK *sock, TASKARG *arg, TTREQ *req){ ttservlog(g_serv, TTLOGDEBUG, "doing out command"); arg->counts[TTSEQNUM*req->idx+TTSEQOUT]++; uint64_t mask = arg->mask; TCADB *adb = arg->adb; TCULOG *ulog = arg->ulog; uint32_t sid = arg->sid; int ksiz = ttsockgetint32(sock); if(ttsockcheckend(sock) || ksiz < 0 || ksiz > MAXARGSIZ){ ttservlog(g_serv, TTLOGINFO, "do_out: invalid parameters"); return; } char stack[TTIOBUFSIZ]; char *buf = (ksiz < TTIOBUFSIZ) ? stack : tcmalloc(ksiz + 1); pthread_cleanup_push(free, (buf == stack) ? NULL : buf); if(ttsockrecv(sock, buf, ksiz) && !ttsockcheckend(sock)){ uint8_t code = 0; if(mask & ((1ULL << TTSEQOUT) | (1ULL << TTSEQALLORG) | (1ULL << TTSEQALLWRITE))){ code = 1; ttservlog(g_serv, TTLOGINFO, "do_out: forbidden"); } else if(!tculogadbout(ulog, sid, 0, adb, buf, ksiz)){ arg->counts[TTSEQNUM*req->idx+TTSEQOUTMISS]++; code = 1; } if(ttsocksend(sock, &code, sizeof(code))){ req->keep = true; } else { ttservlog(g_serv, TTLOGINFO, "do_out: response failed"); } } else { ttservlog(g_serv, TTLOGINFO, "do_out: invalid entity"); } pthread_cleanup_pop(1); } /* handle the get command */ static void do_get(TTSOCK *sock, TASKARG *arg, TTREQ *req){ ttservlog(g_serv, TTLOGDEBUG, "doing get command"); arg->counts[TTSEQNUM*req->idx+TTSEQGET]++; uint64_t mask = arg->mask; TCADB *adb = arg->adb; int ksiz = ttsockgetint32(sock); if(ttsockcheckend(sock) || ksiz < 0 || ksiz > MAXARGSIZ){ ttservlog(g_serv, TTLOGINFO, "do_get: invalid parameters"); return; } char stack[TTIOBUFSIZ]; char *buf = (ksiz < TTIOBUFSIZ) ? stack : tcmalloc(ksiz + 1); pthread_cleanup_push(free, (buf == stack) ? NULL : buf); if(ttsockrecv(sock, buf, ksiz) && !ttsockcheckend(sock)){ char *vbuf; int vsiz; if(mask & ((1ULL << TTSEQGET) | (1ULL << TTSEQALLORG) | (1ULL << TTSEQALLREAD))){ vbuf = NULL; vsiz = 0; ttservlog(g_serv, TTLOGINFO, "do_get: forbidden"); } else { vbuf = tcadbget(adb, buf, ksiz, &vsiz); } if(vbuf){ int rsiz = vsiz + sizeof(uint8_t) + sizeof(uint32_t); char *rbuf = (rsiz < TTIOBUFSIZ) ? stack : tcmalloc(rsiz); pthread_cleanup_push(free, (rbuf == stack) ? NULL : rbuf); *rbuf = 0; uint32_t num; num = TTHTONL((uint32_t)vsiz); memcpy(rbuf + sizeof(uint8_t), &num, sizeof(uint32_t)); memcpy(rbuf + sizeof(uint8_t) + sizeof(uint32_t), vbuf, vsiz); tcfree(vbuf); if(ttsocksend(sock, rbuf, rsiz)){ req->keep = true; } else { ttservlog(g_serv, TTLOGINFO, "do_get: response failed"); } pthread_cleanup_pop(1); } else { arg->counts[TTSEQNUM*req->idx+TTSEQGETMISS]++; uint8_t code = 1; if(ttsocksend(sock, &code, sizeof(code))){ req->keep = true; } else { ttservlog(g_serv, TTLOGINFO, "do_get: response failed"); } } } else { ttservlog(g_serv, TTLOGINFO, "do_get: invalid entity"); } pthread_cleanup_pop(1); } /* handle the mget command */ static void do_mget(TTSOCK *sock, TASKARG *arg, TTREQ *req){ ttservlog(g_serv, TTLOGDEBUG, "doing mget command"); arg->counts[TTSEQNUM*req->idx+TTSEQMGET]++; uint64_t mask = arg->mask; TCADB *adb = arg->adb; int rnum = ttsockgetint32(sock); if(ttsockcheckend(sock) || rnum < 0 || rnum > MAXARGNUM){ ttservlog(g_serv, TTLOGINFO, "do_mget: invalid parameters"); return; } TCLIST *keys = tclistnew2(rnum); pthread_cleanup_push((void (*)(void *))tclistdel, keys); char stack[TTIOBUFSIZ]; for(int i = 0; i < rnum; i++){ int ksiz = ttsockgetint32(sock); if(ttsockcheckend(sock) || ksiz < 0 || ksiz > MAXARGSIZ) break; char *buf = (ksiz < TTIOBUFSIZ) ? stack : tcmalloc(ksiz + 1); pthread_cleanup_push(free, (buf == stack) ? NULL : buf); if(ttsockrecv(sock, buf, ksiz)) tclistpush(keys, buf, ksiz); pthread_cleanup_pop(1); } if(!ttsockcheckend(sock)){ TCXSTR *xstr = tcxstrnew(); pthread_cleanup_push((void (*)(void *))tcxstrdel, xstr); uint8_t code = 0; tcxstrcat(xstr, &code, sizeof(code)); uint32_t num = 0; tcxstrcat(xstr, &num, sizeof(num)); rnum = 0; if(mask & ((1ULL << TTSEQMGET) | (1ULL << TTSEQALLORG) | (1ULL << TTSEQALLREAD))){ ttservlog(g_serv, TTLOGINFO, "do_mget: forbidden"); } else { for(int i = 0; i < tclistnum(keys); i++){ int ksiz; const char *kbuf = tclistval(keys, i, &ksiz); int vsiz; char *vbuf = tcadbget(adb, kbuf, ksiz, &vsiz); if(vbuf){ num = TTHTONL((uint32_t)ksiz); tcxstrcat(xstr, &num, sizeof(num)); num = TTHTONL((uint32_t)vsiz); tcxstrcat(xstr, &num, sizeof(num)); tcxstrcat(xstr, kbuf, ksiz); tcxstrcat(xstr, vbuf, vsiz); tcfree(vbuf); rnum++; } } } num = TTHTONL((uint32_t)rnum); memcpy((char *)tcxstrptr(xstr) + sizeof(code), &num, sizeof(num)); if(ttsocksend(sock, tcxstrptr(xstr), tcxstrsize(xstr))){ req->keep = true; } else { ttservlog(g_serv, TTLOGINFO, "do_mget: response failed"); } pthread_cleanup_pop(1); } else { ttservlog(g_serv, TTLOGINFO, "do_mget: invalid entity"); } pthread_cleanup_pop(1); } /* handle the vsiz command */ static void do_vsiz(TTSOCK *sock, TASKARG *arg, TTREQ *req){ ttservlog(g_serv, TTLOGDEBUG, "doing vsiz command"); arg->counts[TTSEQNUM*req->idx+TTSEQVSIZ]++; uint64_t mask = arg->mask; TCADB *adb = arg->adb; int ksiz = ttsockgetint32(sock); if(ttsockcheckend(sock) || ksiz < 0 || ksiz > MAXARGSIZ){ ttservlog(g_serv, TTLOGINFO, "do_vsiz: invalid parameters"); return; } char stack[TTIOBUFSIZ]; char *buf = (ksiz < TTIOBUFSIZ) ? stack : tcmalloc(ksiz + 1); pthread_cleanup_push(free, (buf == stack) ? NULL : buf); if(ttsockrecv(sock, buf, ksiz) && !ttsockcheckend(sock)){ int vsiz; if(mask & ((1ULL << TTSEQVSIZ) | (1ULL << TTSEQALLORG) | (1ULL << TTSEQALLREAD))){ vsiz = -1; ttservlog(g_serv, TTLOGINFO, "do_vsiz: forbidden"); } else { vsiz = tcadbvsiz(adb, buf, ksiz); } if(vsiz >= 0){ *stack = 0; uint32_t num; num = TTHTONL((uint32_t)vsiz); memcpy(stack + sizeof(uint8_t), &num, sizeof(uint32_t)); if(ttsocksend(sock, stack, sizeof(uint8_t) + sizeof(uint32_t))){ req->keep = true; } else { ttservlog(g_serv, TTLOGINFO, "do_vsiz: response failed"); } } else { uint8_t code = 1; if(ttsocksend(sock, &code, sizeof(code))){ req->keep = true; } else { ttservlog(g_serv, TTLOGINFO, "do_vsiz: response failed"); } } } else { ttservlog(g_serv, TTLOGINFO, "do_vsiz: invalid entity"); } pthread_cleanup_pop(1); } /* handle the iterinit command */ static void do_iterinit(TTSOCK *sock, TASKARG *arg, TTREQ *req){ ttservlog(g_serv, TTLOGDEBUG, "doing iterinit command"); arg->counts[TTSEQNUM*req->idx+TTSEQITERINIT]++; uint64_t mask = arg->mask; TCADB *adb = arg->adb; uint8_t code = 0; if(mask & ((1ULL << TTSEQITERINIT) | (1ULL << TTSEQALLORG) | (1ULL << TTSEQALLREAD))){ code = 1; ttservlog(g_serv, TTLOGINFO, "do_iterinit: forbidden"); } else if(!tcadbiterinit(adb)){ code = 1; ttservlog(g_serv, TTLOGERROR, "do_iterinit: operation failed"); } if(ttsocksend(sock, &code, sizeof(code))){ req->keep = true; } else { ttservlog(g_serv, TTLOGINFO, "do_iterinit: response failed"); } } /* handle the iternext command */ static void do_iternext(TTSOCK *sock, TASKARG *arg, TTREQ *req){ ttservlog(g_serv, TTLOGDEBUG, "doing iternext command"); arg->counts[TTSEQNUM*req->idx+TTSEQITERNEXT]++; uint64_t mask = arg->mask; TCADB *adb = arg->adb; int vsiz; char *vbuf; if(mask & ((1ULL << TTSEQITERNEXT) | (1ULL << TTSEQALLORG) | (1ULL << TTSEQALLREAD))){ vbuf = NULL; vsiz = 0; ttservlog(g_serv, TTLOGINFO, "do_iternext: forbidden"); } else { vbuf = tcadbiternext(adb, &vsiz); } if(vbuf){ int rsiz = vsiz + sizeof(uint8_t) + sizeof(uint32_t); char stack[TTIOBUFSIZ]; char *rbuf = (rsiz < TTIOBUFSIZ) ? stack : tcmalloc(rsiz); pthread_cleanup_push(free, (rbuf == stack) ? NULL : rbuf); *rbuf = 0; uint32_t num; num = TTHTONL((uint32_t)vsiz); memcpy(rbuf + sizeof(uint8_t), &num, sizeof(uint32_t)); memcpy(rbuf + sizeof(uint8_t) + sizeof(uint32_t), vbuf, vsiz); tcfree(vbuf); if(ttsocksend(sock, rbuf, rsiz)){ req->keep = true; } else { ttservlog(g_serv, TTLOGINFO, "do_iternext: response failed"); } pthread_cleanup_pop(1); } else { uint8_t code = 1; if(ttsocksend(sock, &code, sizeof(code))){ req->keep = true; } else { ttservlog(g_serv, TTLOGINFO, "do_iternext: response failed"); } } } /* handle the fwmkeys command */ static void do_fwmkeys(TTSOCK *sock, TASKARG *arg, TTREQ *req){ ttservlog(g_serv, TTLOGDEBUG, "doing fwmkeys command"); arg->counts[TTSEQNUM*req->idx+TTSEQFWMKEYS]++; uint64_t mask = arg->mask; TCADB *adb = arg->adb; int psiz = ttsockgetint32(sock); int max = ttsockgetint32(sock); if(ttsockcheckend(sock) || psiz < 0 || psiz > MAXARGSIZ){ ttservlog(g_serv, TTLOGINFO, "do_fwmkeys: invalid parameters"); return; } char stack[TTIOBUFSIZ]; char *buf = (psiz < TTIOBUFSIZ) ? stack : tcmalloc(psiz + 1); pthread_cleanup_push(free, (buf == stack) ? NULL : buf); if(ttsockrecv(sock, buf, psiz) && !ttsockcheckend(sock)){ TCLIST *keys = tcadbfwmkeys(adb, buf, psiz, max); pthread_cleanup_push((void (*)(void *))tclistdel, keys); TCXSTR *xstr = tcxstrnew(); pthread_cleanup_push((void (*)(void *))tcxstrdel, xstr); uint8_t code = 0; tcxstrcat(xstr, &code, sizeof(code)); uint32_t num = 0; tcxstrcat(xstr, &num, sizeof(num)); int knum = 0; if(mask & ((1ULL << TTSEQFWMKEYS) | (1ULL << TTSEQALLORG) | (1ULL << TTSEQALLREAD))){ ttservlog(g_serv, TTLOGINFO, "do_fwmkeys: forbidden"); } else { for(int i = 0; i < tclistnum(keys); i++){ int ksiz; const char *kbuf = tclistval(keys, i, &ksiz); num = TTHTONL((uint32_t)ksiz); tcxstrcat(xstr, &num, sizeof(num)); tcxstrcat(xstr, kbuf, ksiz); knum++; } } num = TTHTONL((uint32_t)knum); memcpy((char *)tcxstrptr(xstr) + sizeof(code), &num, sizeof(num)); if(ttsocksend(sock, tcxstrptr(xstr), tcxstrsize(xstr))){ req->keep = true; } else { ttservlog(g_serv, TTLOGINFO, "do_fwmkeys: response failed"); } pthread_cleanup_pop(1); pthread_cleanup_pop(1); } else { ttservlog(g_serv, TTLOGINFO, "do_fwmkeys: invalid entity"); } pthread_cleanup_pop(1); } /* handle the addint command */ static void do_addint(TTSOCK *sock, TASKARG *arg, TTREQ *req){ ttservlog(g_serv, TTLOGDEBUG, "doing addint command"); arg->counts[TTSEQNUM*req->idx+TTSEQADDINT]++; uint64_t mask = arg->mask; TCADB *adb = arg->adb; TCULOG *ulog = arg->ulog; uint32_t sid = arg->sid; int ksiz = ttsockgetint32(sock); int anum = ttsockgetint32(sock); if(ttsockcheckend(sock) || ksiz < 0 || ksiz > MAXARGSIZ){ ttservlog(g_serv, TTLOGINFO, "do_addint: invalid parameters"); return; } char stack[TTIOBUFSIZ]; char *buf = (ksiz < TTIOBUFSIZ) ? stack : tcmalloc(ksiz + 1); pthread_cleanup_push(free, (buf == stack) ? NULL : buf); if(ttsockrecv(sock, buf, ksiz) && !ttsockcheckend(sock)){ int snum; if(mask & ((1ULL << TTSEQADDINT) | (1ULL << TTSEQALLORG) | (1ULL << TTSEQALLWRITE))){ snum = INT_MIN; ttservlog(g_serv, TTLOGINFO, "do_addint: forbidden"); } else { snum = tculogadbaddint(ulog, sid, 0, adb, buf, ksiz, anum); } if(snum != INT_MIN){ *stack = 0; uint32_t num; num = TTHTONL((uint32_t)snum); memcpy(stack + sizeof(uint8_t), &num, sizeof(uint32_t)); if(ttsocksend(sock, stack, sizeof(uint8_t) + sizeof(uint32_t))){ req->keep = true; } else { ttservlog(g_serv, TTLOGINFO, "do_addint: response failed"); } } else { uint8_t code = 1; if(ttsocksend(sock, &code, sizeof(code))){ req->keep = true; } else { ttservlog(g_serv, TTLOGINFO, "do_addint: response failed"); } } } else { ttservlog(g_serv, TTLOGINFO, "do_addint: invalid entity"); } pthread_cleanup_pop(1); } /* handle the adddouble command */ static void do_adddouble(TTSOCK *sock, TASKARG *arg, TTREQ *req){ ttservlog(g_serv, TTLOGDEBUG, "doing adddouble command"); arg->counts[TTSEQNUM*req->idx+TTSEQADDDOUBLE]++; uint64_t mask = arg->mask; TCADB *adb = arg->adb; TCULOG *ulog = arg->ulog; uint32_t sid = arg->sid; int ksiz = ttsockgetint32(sock); char abuf[sizeof(uint64_t)*2]; if(!ttsockrecv(sock, abuf, sizeof(abuf)) || ttsockcheckend(sock) || ksiz < 0 || ksiz > MAXARGSIZ){ ttservlog(g_serv, TTLOGINFO, "do_adddouble: invalid parameters"); return; } double anum = ttunpackdouble(abuf); char stack[TTIOBUFSIZ]; char *buf = (ksiz < TTIOBUFSIZ) ? stack : tcmalloc(ksiz + 1); pthread_cleanup_push(free, (buf == stack) ? NULL : buf); if(ttsockrecv(sock, buf, ksiz) && !ttsockcheckend(sock)){ double snum; if(mask & ((1ULL << TTSEQADDDOUBLE) | (1ULL << TTSEQALLORG) | (1ULL << TTSEQALLWRITE))){ snum = nan(""); ttservlog(g_serv, TTLOGINFO, "do_adddouble: forbidden"); } else { snum = tculogadbadddouble(ulog, sid, 0, adb, buf, ksiz, anum); } if(!isnan(snum)){ *stack = 0; ttpackdouble(snum, abuf); memcpy(stack + sizeof(uint8_t), abuf, sizeof(abuf)); if(ttsocksend(sock, stack, sizeof(uint8_t) + sizeof(abuf))){ req->keep = true; } else { ttservlog(g_serv, TTLOGINFO, "do_adddouble: response failed"); } } else { uint8_t code = 1; if(ttsocksend(sock, &code, sizeof(code))){ req->keep = true; } else { ttservlog(g_serv, TTLOGINFO, "do_adddouble: response failed"); } } } else { ttservlog(g_serv, TTLOGINFO, "do_adddouble: invalid entity"); } pthread_cleanup_pop(1); } /* handle the ext command */ static void do_ext(TTSOCK *sock, TASKARG *arg, TTREQ *req){ ttservlog(g_serv, TTLOGDEBUG, "doing ext command"); arg->counts[TTSEQNUM*req->idx+TTSEQEXT]++; uint64_t mask = arg->mask; pthread_mutex_t *rmtxs = arg->rmtxs; void *scr = arg->screxts[req->idx]; int nsiz = ttsockgetint32(sock); int opts = ttsockgetint32(sock); int ksiz = ttsockgetint32(sock); int vsiz = ttsockgetint32(sock); if(ttsockcheckend(sock) || nsiz < 0 || nsiz >= TTADDRBUFSIZ || ksiz < 0 || ksiz > MAXARGSIZ || vsiz < 0 || vsiz > MAXARGSIZ){ ttservlog(g_serv, TTLOGINFO, "do_ext: invalid parameters"); return; } int rsiz = nsiz + ksiz + vsiz; char stack[TTIOBUFSIZ]; char *buf = (rsiz < TTIOBUFSIZ) ? stack : tcmalloc(rsiz + 1); pthread_cleanup_push(free, (buf == stack) ? NULL : buf); if(ttsockrecv(sock, buf, rsiz) && !ttsockcheckend(sock)){ char name[TTADDRBUFSIZ]; memcpy(name, buf, nsiz); name[nsiz] = '\0'; const char *kbuf = buf + nsiz; const char *vbuf = kbuf + ksiz; int xsiz = 0; char *xbuf = NULL; if(mask & ((1ULL << TTSEQEXT) | (1ULL << TTSEQALLORG))){ ttservlog(g_serv, TTLOGINFO, "do_ext: forbidden"); } else if(scr){ if(opts & RDBXOLCKGLB){ bool err = false; for(int i = 0; i < RECMTXNUM; i++){ if(pthread_mutex_lock(rmtxs + i) != 0){ ttservlog(g_serv, TTLOGERROR, "do_ext: pthread_mutex_lock failed"); while(--i >= 0){ pthread_mutex_unlock(rmtxs + i); } err = true; break; } } if(!err){ xbuf = scrextcallmethod(scr, name, kbuf, ksiz, vbuf, vsiz, &xsiz); for(int i = RECMTXNUM - 1; i >= 0; i--){ if(pthread_mutex_unlock(rmtxs + i) != 0) ttservlog(g_serv, TTLOGERROR, "do_ext: pthread_mutex_unlock failed"); } } } else if(opts & RDBXOLCKREC){ int mtxidx = recmtxidx(kbuf, ksiz); if(pthread_mutex_lock(rmtxs + mtxidx) == 0){ xbuf = scrextcallmethod(scr, name, kbuf, ksiz, vbuf, vsiz, &xsiz); if(pthread_mutex_unlock(rmtxs + mtxidx) != 0) ttservlog(g_serv, TTLOGERROR, "do_ext: pthread_mutex_unlock failed"); } else { ttservlog(g_serv, TTLOGERROR, "do_ext: pthread_mutex_lock failed"); } } else { xbuf = scrextcallmethod(scr, name, kbuf, ksiz, vbuf, vsiz, &xsiz); } } if(xbuf){ int rsiz = xsiz + sizeof(uint8_t) + sizeof(uint32_t); char *rbuf = (rsiz < TTIOBUFSIZ) ? stack : tcmalloc(rsiz); pthread_cleanup_push(free, (rbuf == stack) ? NULL : rbuf); *rbuf = 0; uint32_t num; num = TTHTONL((uint32_t)xsiz); memcpy(rbuf + sizeof(uint8_t), &num, sizeof(uint32_t)); memcpy(rbuf + sizeof(uint8_t) + sizeof(uint32_t), xbuf, xsiz); tcfree(xbuf); if(ttsocksend(sock, rbuf, rsiz)){ req->keep = true; } else { ttservlog(g_serv, TTLOGINFO, "do_ext: response failed"); } pthread_cleanup_pop(1); } else { uint8_t code = 1; if(ttsocksend(sock, &code, sizeof(code))){ req->keep = true; } else { ttservlog(g_serv, TTLOGINFO, "do_ext: response failed"); } } } else { ttservlog(g_serv, TTLOGINFO, "do_ext: invalid entity"); } pthread_cleanup_pop(1); } /* handle the sync command */ static void do_sync(TTSOCK *sock, TASKARG *arg, TTREQ *req){ ttservlog(g_serv, TTLOGINFO, "doing sync command"); arg->counts[TTSEQNUM*req->idx+TTSEQSYNC]++; uint64_t mask = arg->mask; TCADB *adb = arg->adb; TCULOG *ulog = arg->ulog; uint32_t sid = arg->sid; uint8_t code = 0; if(mask & ((1ULL << TTSEQSYNC) | (1ULL << TTSEQALLORG) | (1ULL << TTSEQALLMANAGE))){ code = 1; ttservlog(g_serv, TTLOGINFO, "do_sync: forbidden"); } else if(!tculogadbsync(ulog, sid, 0, adb)){ code = 1; ttservlog(g_serv, TTLOGERROR, "do_sync: operation failed"); } if(ttsocksend(sock, &code, sizeof(code))){ req->keep = true; } else { ttservlog(g_serv, TTLOGINFO, "do_sync: response failed"); } } /* handle the optimize command */ static void do_optimize(TTSOCK *sock, TASKARG *arg, TTREQ *req){ ttservlog(g_serv, TTLOGINFO, "doing optimize command"); arg->counts[TTSEQNUM*req->idx+TTSEQOPTIMIZE]++; uint64_t mask = arg->mask; TCADB *adb = arg->adb; TCULOG *ulog = arg->ulog; uint32_t sid = arg->sid; int psiz = ttsockgetint32(sock); if(ttsockcheckend(sock) || psiz < 0 || psiz > MAXARGSIZ){ ttservlog(g_serv, TTLOGINFO, "do_optimize: invalid parameters"); return; } char stack[TTIOBUFSIZ]; char *buf = (psiz < TTIOBUFSIZ) ? stack : tcmalloc(psiz + 1); pthread_cleanup_push(free, (buf == stack) ? NULL : buf); if(ttsockrecv(sock, buf, psiz) && !ttsockcheckend(sock)){ buf[psiz] = '\0'; uint8_t code = 0; if(mask & ((1ULL << TTSEQOPTIMIZE) | (1ULL << TTSEQALLORG) | (1ULL << TTSEQALLMANAGE))){ code = 1; ttservlog(g_serv, TTLOGINFO, "do_optimize: forbidden"); } else if(!tculogadboptimize(ulog, sid, 0, adb, buf)){ code = 1; } if(ttsocksend(sock, &code, sizeof(code))){ req->keep = true; } else { ttservlog(g_serv, TTLOGINFO, "do_optimize: response failed"); } } else { ttservlog(g_serv, TTLOGINFO, "do_optimize: invalid entity"); } pthread_cleanup_pop(1); } /* handle the vanish command */ static void do_vanish(TTSOCK *sock, TASKARG *arg, TTREQ *req){ ttservlog(g_serv, TTLOGINFO, "doing vanish command"); arg->counts[TTSEQNUM*req->idx+TTSEQVANISH]++; uint64_t mask = arg->mask; TCADB *adb = arg->adb; TCULOG *ulog = arg->ulog; uint32_t sid = arg->sid; uint8_t code = 0; if(mask & ((1ULL << TTSEQVANISH) | (1ULL << TTSEQALLORG) | (1ULL << TTSEQALLWRITE))){ code = 1; ttservlog(g_serv, TTLOGINFO, "do_vanish: forbidden"); } else if(!tculogadbvanish(ulog, sid, 0, adb)){ code = 1; ttservlog(g_serv, TTLOGERROR, "do_vanish: operation failed"); } if(ttsocksend(sock, &code, sizeof(code))){ req->keep = true; } else { ttservlog(g_serv, TTLOGINFO, "do_vanish: response failed"); } } /* handle the copy command */ static void do_copy(TTSOCK *sock, TASKARG *arg, TTREQ *req){ ttservlog(g_serv, TTLOGINFO, "doing copy command"); arg->counts[TTSEQNUM*req->idx+TTSEQCOPY]++; uint64_t mask = arg->mask; TCADB *adb = arg->adb; int psiz = ttsockgetint32(sock); if(ttsockcheckend(sock) || psiz < 0 || psiz > MAXARGSIZ){ ttservlog(g_serv, TTLOGINFO, "do_copy: invalid parameters"); return; } char stack[TTIOBUFSIZ]; char *buf = (psiz < TTIOBUFSIZ) ? stack : tcmalloc(psiz + 1); pthread_cleanup_push(free, (buf == stack) ? NULL : buf); if(ttsockrecv(sock, buf, psiz) && !ttsockcheckend(sock)){ buf[psiz] = '\0'; uint8_t code = 0; if(mask & ((1ULL << TTSEQCOPY) | (1ULL << TTSEQALLORG) | (1ULL << TTSEQALLMANAGE))){ code = 1; ttservlog(g_serv, TTLOGINFO, "do_copy: forbidden"); } else if(!tcadbcopy(adb, buf)){ code = 1; ttservlog(g_serv, TTLOGERROR, "do_copy: operation failed"); } if(ttsocksend(sock, &code, sizeof(code))){ req->keep = true; } else { ttservlog(g_serv, TTLOGINFO, "do_copy: response failed"); } } else { ttservlog(g_serv, TTLOGINFO, "do_copy: invalid entity"); } pthread_cleanup_pop(1); } /* handle the restore command */ static void do_restore(TTSOCK *sock, TASKARG *arg, TTREQ *req){ ttservlog(g_serv, TTLOGINFO, "doing restore command"); arg->counts[TTSEQNUM*req->idx+TTSEQRESTORE]++; uint64_t mask = arg->mask; TCADB *adb = arg->adb; TCULOG *ulog = arg->ulog; int psiz = ttsockgetint32(sock); uint64_t ts = ttsockgetint64(sock); int opts = ttsockgetint32(sock); if(ttsockcheckend(sock) || psiz < 0 || psiz > MAXARGSIZ){ ttservlog(g_serv, TTLOGINFO, "do_restore: invalid parameters"); return; } char stack[TTIOBUFSIZ]; char *buf = (psiz < TTIOBUFSIZ) ? stack : tcmalloc(psiz + 1); pthread_cleanup_push(free, (buf == stack) ? NULL : buf); if(ttsockrecv(sock, buf, psiz) && !ttsockcheckend(sock)){ buf[psiz] = '\0'; bool con = (opts & RDBROCHKCON) != 0; uint8_t code = 0; if(mask & ((1ULL << TTSEQRESTORE) | (1ULL << TTSEQALLORG) | (1ULL << TTSEQALLMANAGE))){ code = 1; ttservlog(g_serv, TTLOGINFO, "do_restore: forbidden"); } else if(!tculogadbrestore(adb, buf, ts, con, ulog)){ code = 1; ttservlog(g_serv, TTLOGERROR, "do_restore: operation failed"); } if(ttsocksend(sock, &code, sizeof(code))){ req->keep = true; } else { ttservlog(g_serv, TTLOGINFO, "do_restore: response failed"); } } else { ttservlog(g_serv, TTLOGINFO, "do_restore: invalid entity"); } pthread_cleanup_pop(1); } /* handle the setmst command */ static void do_setmst(TTSOCK *sock, TASKARG *arg, TTREQ *req){ ttservlog(g_serv, TTLOGINFO, "doing setmst command"); arg->counts[TTSEQNUM*req->idx+TTSEQSETMST]++; uint64_t mask = arg->mask; REPLARG *sarg = arg->sarg; int hsiz = ttsockgetint32(sock); int port = ttsockgetint32(sock); uint64_t ts = ttsockgetint64(sock); int opts = ttsockgetint32(sock); if(ttsockcheckend(sock) || hsiz < 0 || hsiz > MAXARGSIZ || port < 0){ ttservlog(g_serv, TTLOGINFO, "do_setmst: invalid parameters"); return; } char stack[TTIOBUFSIZ]; char *buf = (hsiz < TTIOBUFSIZ) ? stack : tcmalloc(hsiz + 1); pthread_cleanup_push(free, (buf == stack) ? NULL : buf); if(ttsockrecv(sock, buf, hsiz) && !ttsockcheckend(sock)){ buf[hsiz] = '\0'; uint8_t code = 0; if(mask & ((1ULL << TTSEQSETMST) | (1ULL << TTSEQALLORG) | (1ULL << TTSEQALLMANAGE))){ code = 1; ttservlog(g_serv, TTLOGINFO, "do_setmst: forbidden"); } else { snprintf(sarg->host, TTADDRBUFSIZ, "%s", buf); sarg->port = port; sarg->opts = opts; sarg->recon = true; sarg->fatal = false; sarg->mts = ts; } if(ttsocksend(sock, &code, sizeof(code))){ req->keep = true; } else { ttservlog(g_serv, TTLOGINFO, "do_setmst: response failed"); } } else { ttservlog(g_serv, TTLOGINFO, "do_setmst: invalid entity"); } pthread_cleanup_pop(1); } /* handle the rnum command */ static void do_rnum(TTSOCK *sock, TASKARG *arg, TTREQ *req){ ttservlog(g_serv, TTLOGDEBUG, "doing rnum command"); arg->counts[TTSEQNUM*req->idx+TTSEQRNUM]++; uint64_t mask = arg->mask; TCADB *adb = arg->adb; char buf[LINEBUFSIZ]; *buf = 0; uint64_t rnum; if(mask & ((1ULL << TTSEQRNUM) | (1ULL << TTSEQALLORG) | (1ULL << TTSEQALLREAD))){ rnum = 0; ttservlog(g_serv, TTLOGINFO, "do_rnum: forbidden"); } else { rnum = tcadbrnum(adb); } rnum = TTHTONLL(rnum); memcpy(buf + sizeof(uint8_t), &rnum, sizeof(uint64_t)); if(ttsocksend(sock, buf, sizeof(uint8_t) + sizeof(uint64_t))){ req->keep = true; } else { ttservlog(g_serv, TTLOGINFO, "do_rnum: response failed"); } } /* handle the size command */ static void do_size(TTSOCK *sock, TASKARG *arg, TTREQ *req){ ttservlog(g_serv, TTLOGDEBUG, "doing size command"); arg->counts[TTSEQNUM*req->idx+TTSEQSIZE]++; uint64_t mask = arg->mask; TCADB *adb = arg->adb; char buf[LINEBUFSIZ]; *buf = 0; uint64_t size; if(mask & ((1ULL << TTSEQSIZE) | (1ULL << TTSEQALLORG) | (1ULL << TTSEQALLREAD))){ size = 0; ttservlog(g_serv, TTLOGINFO, "do_size: forbidden"); } else { size = tcadbsize(adb); } size = TTHTONLL(size); memcpy(buf + sizeof(uint8_t), &size, sizeof(uint64_t)); if(ttsocksend(sock, buf, sizeof(uint8_t) + sizeof(uint64_t))){ req->keep = true; } else { ttservlog(g_serv, TTLOGINFO, "do_size: response failed"); } } /* handle the stat command */ static void do_stat(TTSOCK *sock, TASKARG *arg, TTREQ *req){ ttservlog(g_serv, TTLOGDEBUG, "doing stat command"); arg->counts[TTSEQNUM*req->idx+TTSEQSTAT]++; uint64_t mask = arg->mask; TCADB *adb = arg->adb; REPLARG *sarg = arg->sarg; char buf[TTIOBUFSIZ]; char *wp = buf + sizeof(uint8_t) + sizeof(uint32_t); if(mask & ((1ULL << TTSEQSTAT) | (1ULL << TTSEQALLORG) | (1ULL << TTSEQALLREAD))){ ttservlog(g_serv, TTLOGINFO, "do_stat: forbidden"); } else { double now = tctime(); wp += sprintf(wp, "version\t%s\n", ttversion); wp += sprintf(wp, "libver\t%d\n", _TT_LIBVER); wp += sprintf(wp, "protver\t%s\n", _TT_PROTVER); wp += sprintf(wp, "os\t%s\n", TTSYSNAME); wp += sprintf(wp, "time\t%.6f\n", now); wp += sprintf(wp, "pid\t%lld\n", (long long)getpid()); wp += sprintf(wp, "sid\t%d\n", arg->sid); switch(tcadbomode(adb)){ case ADBOVOID: wp += sprintf(wp, "type\tvoid\n"); break; case ADBOMDB: wp += sprintf(wp, "type\ton-memory hash\n"); break; case ADBONDB: wp += sprintf(wp, "type\ton-memory tree\n"); break; case ADBOHDB: wp += sprintf(wp, "type\thash\n"); break; case ADBOBDB: wp += sprintf(wp, "type\tB+ tree\n"); break; case ADBOFDB: wp += sprintf(wp, "type\tfixed-length\n"); break; case ADBOTDB: wp += sprintf(wp, "type\ttable\n"); break; case ADBOSKEL: wp += sprintf(wp, "type\tskeleton\n"); break; } const char *path = tcadbpath(adb); if(path) wp += sprintf(wp, "path\t%s\n", path); wp += sprintf(wp, "rnum\t%llu\n", (unsigned long long)tcadbrnum(adb)); wp += sprintf(wp, "size\t%llu\n", (unsigned long long)tcadbsize(adb)); TCLIST *args = tclistnew2(1); pthread_cleanup_push((void (*)(void *))tclistdel, args); TCLIST *res = tcadbmisc(adb, "error", args); if(res){ int rnum = tclistnum(res); const char *emsg = NULL; bool fatal = false; for(int i = 0; i < rnum; i++){ const char *vbuf = tclistval2(res, i); if(!tcstricmp(vbuf, "fatal")){ fatal = true; } else { emsg = vbuf; } } if(fatal) wp += sprintf(wp, "fatal\t%s\n", emsg); tclistdel(res); } pthread_cleanup_pop(1); wp += sprintf(wp, "bigend\t%d\n", TTBIGEND); if(sarg->host[0] != '\0'){ wp += sprintf(wp, "mhost\t%s\n", sarg->host); wp += sprintf(wp, "mport\t%d\n", sarg->port); wp += sprintf(wp, "rts\t%llu\n", (unsigned long long)sarg->rts); double delay = now - sarg->rts / 1000000.0; wp += sprintf(wp, "delay\t%.6f\n", delay >= 0 ? delay : 0.0); } wp += sprintf(wp, "fd\t%d\n", sock->fd); wp += sprintf(wp, "loadavg\t%.6f\n", ttgetloadavg()); TCMAP *info = tcsysinfo(); if(info){ const char *vbuf = tcmapget2(info, "size"); if(vbuf) wp += sprintf(wp, "memsize\t%s\n", vbuf); vbuf = tcmapget2(info, "rss"); if(vbuf) wp += sprintf(wp, "memrss\t%s\n", vbuf); vbuf = tcmapget2(info, "utime"); if(vbuf) wp += sprintf(wp, "ru_user\t%s\n", vbuf); vbuf = tcmapget2(info, "stime"); if(vbuf) wp += sprintf(wp, "ru_sys\t%s\n", vbuf); tcmapdel(info); } wp += sprintf(wp, "ru_real\t%.6f\n", now - g_starttime); } wp += sprintf(wp, "cnt_put\t%llu\n", (unsigned long long)sumstat(arg, TTSEQPUT)); wp += sprintf(wp, "cnt_putkeep\t%llu\n", (unsigned long long)sumstat(arg, TTSEQPUTKEEP)); wp += sprintf(wp, "cnt_putcat\t%llu\n", (unsigned long long)sumstat(arg, TTSEQPUTCAT)); wp += sprintf(wp, "cnt_putshl\t%llu\n", (unsigned long long)sumstat(arg, TTSEQPUTSHL)); wp += sprintf(wp, "cnt_putnr\t%llu\n", (unsigned long long)sumstat(arg, TTSEQPUTNR)); wp += sprintf(wp, "cnt_out\t%llu\n", (unsigned long long)sumstat(arg, TTSEQOUT)); wp += sprintf(wp, "cnt_get\t%llu\n", (unsigned long long)sumstat(arg, TTSEQGET)); wp += sprintf(wp, "cnt_mget\t%llu\n", (unsigned long long)sumstat(arg, TTSEQMGET)); wp += sprintf(wp, "cnt_vsiz\t%llu\n", (unsigned long long)sumstat(arg, TTSEQVSIZ)); wp += sprintf(wp, "cnt_iterinit\t%llu\n", (unsigned long long)sumstat(arg, TTSEQITERINIT)); wp += sprintf(wp, "cnt_iternext\t%llu\n", (unsigned long long)sumstat(arg, TTSEQITERNEXT)); wp += sprintf(wp, "cnt_fwmkeys\t%llu\n", (unsigned long long)sumstat(arg, TTSEQFWMKEYS)); wp += sprintf(wp, "cnt_addint\t%llu\n", (unsigned long long)sumstat(arg, TTSEQADDINT)); wp += sprintf(wp, "cnt_adddouble\t%llu\n", (unsigned long long)sumstat(arg, TTSEQADDDOUBLE)); wp += sprintf(wp, "cnt_ext\t%llu\n", (unsigned long long)sumstat(arg, TTSEQEXT)); wp += sprintf(wp, "cnt_sync\t%llu\n", (unsigned long long)sumstat(arg, TTSEQSYNC)); wp += sprintf(wp, "cnt_optimize\t%llu\n", (unsigned long long)sumstat(arg, TTSEQOPTIMIZE)); wp += sprintf(wp, "cnt_vanish\t%llu\n", (unsigned long long)sumstat(arg, TTSEQVANISH)); wp += sprintf(wp, "cnt_copy\t%llu\n", (unsigned long long)sumstat(arg, TTSEQCOPY)); wp += sprintf(wp, "cnt_restore\t%llu\n", (unsigned long long)sumstat(arg, TTSEQRESTORE)); wp += sprintf(wp, "cnt_setmst\t%llu\n", (unsigned long long)sumstat(arg, TTSEQSETMST)); wp += sprintf(wp, "cnt_rnum\t%llu\n", (unsigned long long)sumstat(arg, TTSEQRNUM)); wp += sprintf(wp, "cnt_size\t%llu\n", (unsigned long long)sumstat(arg, TTSEQSIZE)); wp += sprintf(wp, "cnt_stat\t%llu\n", (unsigned long long)sumstat(arg, TTSEQSTAT)); wp += sprintf(wp, "cnt_misc\t%llu\n", (unsigned long long)sumstat(arg, TTSEQMISC)); wp += sprintf(wp, "cnt_repl\t%llu\n", (unsigned long long)sumstat(arg, TTSEQREPL)); wp += sprintf(wp, "cnt_put_miss\t%llu\n", (unsigned long long)sumstat(arg, TTSEQPUTMISS)); wp += sprintf(wp, "cnt_out_miss\t%llu\n", (unsigned long long)sumstat(arg, TTSEQOUTMISS)); wp += sprintf(wp, "cnt_get_miss\t%llu\n", (unsigned long long)sumstat(arg, TTSEQGETMISS)); *buf = 0; uint32_t size = wp - buf - (sizeof(uint8_t) + sizeof(uint32_t)); size = TTHTONL(size); memcpy(buf + sizeof(uint8_t), &size, sizeof(uint32_t)); if(ttsocksend(sock, buf, wp - buf)){ req->keep = true; } else { ttservlog(g_serv, TTLOGINFO, "do_stat: response failed"); } } /* handle the misc command */ static void do_misc(TTSOCK *sock, TASKARG *arg, TTREQ *req){ ttservlog(g_serv, TTLOGDEBUG, "doing misc command"); arg->counts[TTSEQNUM*req->idx+TTSEQMISC]++; uint64_t mask = arg->mask; TCADB *adb = arg->adb; TCULOG *ulog = arg->ulog; uint32_t sid = arg->sid; int nsiz = ttsockgetint32(sock); int opts = ttsockgetint32(sock); int rnum = ttsockgetint32(sock); if(ttsockcheckend(sock) || nsiz < 0 || nsiz >= TTADDRBUFSIZ || rnum < 0 || rnum > MAXARGNUM){ ttservlog(g_serv, TTLOGINFO, "do_misc: invalid parameters"); return; } char name[TTADDRBUFSIZ]; if(!ttsockrecv(sock, name, nsiz) && !ttsockcheckend(sock)){ ttservlog(g_serv, TTLOGINFO, "do_misc: invalid parameters"); return; } name[nsiz] = '\0'; TCLIST *args = tclistnew2(rnum); pthread_cleanup_push((void (*)(void *))tclistdel, args); char stack[TTIOBUFSIZ]; for(int i = 0; i < rnum; i++){ int rsiz = ttsockgetint32(sock); if(ttsockcheckend(sock) || rsiz < 0 || rsiz > MAXARGSIZ) break; char *buf = (rsiz < TTIOBUFSIZ) ? stack : tcmalloc(rsiz + 1); pthread_cleanup_push(free, (buf == stack) ? NULL : buf); if(ttsockrecv(sock, buf, rsiz)) tclistpush(args, buf, rsiz); pthread_cleanup_pop(1); } if(!ttsockcheckend(sock)){ TCXSTR *xstr = tcxstrnew(); pthread_cleanup_push((void (*)(void *))tcxstrdel, xstr); uint8_t code = 0; tcxstrcat(xstr, &code, sizeof(code)); uint32_t num = 0; tcxstrcat(xstr, &num, sizeof(num)); rnum = 0; if(mask & ((1ULL << TTSEQMISC) | (1ULL << TTSEQALLORG) | (1ULL << TTSEQALLWRITE))){ ttservlog(g_serv, TTLOGINFO, "do_misc: forbidden"); } else { TCLIST *res = (opts & RDBMONOULOG) ? tcadbmisc(adb, name, args) : tculogadbmisc(ulog, sid, 0, adb, name, args); if(res){ for(int i = 0; i < tclistnum(res); i++){ int esiz; const char *ebuf = tclistval(res, i, &esiz); num = TTHTONL((uint32_t)esiz); tcxstrcat(xstr, &num, sizeof(num)); tcxstrcat(xstr, ebuf, esiz); rnum++; } tclistdel(res); } else { *(uint8_t *)tcxstrptr(xstr) = 1; } } num = TTHTONL((uint32_t)rnum); memcpy((char *)tcxstrptr(xstr) + sizeof(code), &num, sizeof(num)); if(ttsocksend(sock, tcxstrptr(xstr), tcxstrsize(xstr))){ req->keep = true; } else { ttservlog(g_serv, TTLOGINFO, "do_misc: response failed"); } pthread_cleanup_pop(1); } else { ttservlog(g_serv, TTLOGINFO, "do_misc: invalid entity"); } pthread_cleanup_pop(1); } /* handle the repl command */ static void do_repl(TTSOCK *sock, TASKARG *arg, TTREQ *req){ ttservlog(g_serv, TTLOGINFO, "doing repl command"); arg->counts[TTSEQNUM*req->idx+TTSEQREPL]++; uint64_t mask = arg->mask; TCULOG *ulog = arg->ulog; uint64_t ts = ttsockgetint64(sock); uint32_t sid = ttsockgetint32(sock); if(ttsockcheckend(sock) || ts < 1 || sid < 1){ ttservlog(g_serv, TTLOGINFO, "do_repl: invalid parameters"); return; } if(mask & (1ULL << TTSEQREPL)){ ttservlog(g_serv, TTLOGINFO, "do_repl: forbidden"); return; } if(sid == arg->sid){ ttservlog(g_serv, TTLOGINFO, "do_repl: rejected circular replication"); return; } uint32_t lnum = TTHTONL(arg->sid); if(!ttsocksend(sock, &lnum, sizeof(lnum))){ ttservlog(g_serv, TTLOGINFO, "do_repl: response failed"); return; } TCULRD *ulrd = tculrdnew(ulog, ts); if(ulrd){ ttservlog(g_serv, TTLOGINFO, "replicating to sid=%u after %llu", (unsigned int)sid, (unsigned long long)ts - 1); pthread_cleanup_push((void (*)(void *))tculrddel, ulrd); bool err = false; double noptime = 0; char stack[TTIOBUFSIZ]; while(!err && !ttserviskilled(g_serv)){ ttsocksetlife(sock, UINT_MAX); double now = tctime(); req->mtime = now + UINT_MAX; if(now - noptime >= 1.0){ *(unsigned char *)stack = TCULMAGICNOP; if(!ttsocksend(sock, stack, sizeof(uint8_t))){ err = true; ttservlog(g_serv, TTLOGINFO, "do_repl: connection closed"); } noptime = now; } tculrdwait(ulrd); uint32_t nopcnt = 0; const char *rbuf; int rsiz; uint64_t rts; uint32_t rsid, rmid; while(!err && (rbuf = tculrdread(ulrd, &rsiz, &rts, &rsid, &rmid)) != NULL){ if(rsid == sid || rmid == sid){ if((nopcnt++ & 0xff) == 0){ now = tctime(); if(now - noptime >= 1.0){ *(unsigned char *)stack = TCULMAGICNOP; if(!ttsocksend(sock, stack, sizeof(uint8_t))){ err = true; ttservlog(g_serv, TTLOGINFO, "do_repl: connection closed"); } noptime = now; } } continue; } int msiz = sizeof(uint8_t) + sizeof(uint64_t) + sizeof(uint32_t) * 2 + rsiz; char *mbuf = (msiz < TTIOBUFSIZ) ? stack : tcmalloc(msiz); pthread_cleanup_push(free, (mbuf == stack) ? NULL : mbuf); unsigned char *wp = (unsigned char *)mbuf; *(wp++) = TCULMAGICNUM; uint64_t llnum = TTHTONLL(rts); memcpy(wp, &llnum, sizeof(llnum)); wp += sizeof(llnum); lnum = TTHTONL(rsid); memcpy(wp, &lnum, sizeof(lnum)); wp += sizeof(lnum); lnum = TTHTONL(rsiz); memcpy(wp, &lnum, sizeof(lnum)); wp += sizeof(lnum); memcpy(wp, rbuf, rsiz); if(!ttsocksend(sock, mbuf, msiz)){ err = true; ttservlog(g_serv, TTLOGINFO, "do_repl: response failed"); } pthread_cleanup_pop(1); } } pthread_cleanup_pop(1); } else { ttservlog(g_serv, TTLOGERROR, "do_repl: tculrdnew failed"); } } /* handle the memcached set command */ static void do_mc_set(TTSOCK *sock, TASKARG *arg, TTREQ *req, char **tokens, int tnum){ ttservlog(g_serv, TTLOGDEBUG, "doing mc_set command"); arg->counts[TTSEQNUM*req->idx+TTSEQPUT]++; uint64_t mask = arg->mask; TCADB *adb = arg->adb; TCULOG *ulog = arg->ulog; uint32_t sid = arg->sid; if(tnum < 5){ ttsockprintf(sock, "CLIENT_ERROR error\r\n"); return; } bool nr = tnum > 5 && !strcmp(tokens[5], "noreply"); const char *kbuf = tokens[1]; int ksiz = strlen(kbuf); int vsiz = tclmax(tcatoi(tokens[4]), 0); char stack[TTIOBUFSIZ]; char *vbuf = (vsiz < TTIOBUFSIZ) ? stack : tcmalloc(vsiz + 1); pthread_cleanup_push(free, (vbuf == stack) ? NULL : vbuf); if(ttsockrecv(sock, vbuf, vsiz) && ttsockgetc(sock) == '\r' && ttsockgetc(sock) == '\n' && !ttsockcheckend(sock)){ int len; if(mask & ((1ULL << TTSEQPUT) | (1ULL << TTSEQALLMC) | (1ULL << TTSEQALLWRITE))){ len = sprintf(stack, "CLIENT_ERROR forbidden\r\n"); ttservlog(g_serv, TTLOGINFO, "do_mc_set: forbidden"); } else if(tculogadbput(ulog, sid, 0, adb, kbuf, ksiz, vbuf, vsiz)){ len = sprintf(stack, "STORED\r\n"); } else { arg->counts[TTSEQNUM*req->idx+TTSEQPUTMISS]++; len = sprintf(stack, "SERVER_ERROR unexpected\r\n"); ttservlog(g_serv, TTLOGERROR, "do_mc_set: operation failed"); } if(nr || ttsocksend(sock, stack, len)){ req->keep = true; } else { ttservlog(g_serv, TTLOGINFO, "do_mc_set: response failed"); } } else { ttservlog(g_serv, TTLOGINFO, "do_mc_set: invalid entity"); } pthread_cleanup_pop(1); } /* handle the memcached add command */ static void do_mc_add(TTSOCK *sock, TASKARG *arg, TTREQ *req, char **tokens, int tnum){ ttservlog(g_serv, TTLOGDEBUG, "doing mc_add command"); arg->counts[TTSEQNUM*req->idx+TTSEQPUTKEEP]++; uint64_t mask = arg->mask; TCADB *adb = arg->adb; TCULOG *ulog = arg->ulog; uint32_t sid = arg->sid; if(tnum < 5){ ttsockprintf(sock, "CLIENT_ERROR error\r\n"); return; } bool nr = tnum > 5 && !strcmp(tokens[5], "noreply"); const char *kbuf = tokens[1]; int ksiz = strlen(kbuf); int vsiz = tclmax(tcatoi(tokens[4]), 0); char stack[TTIOBUFSIZ]; char *vbuf = (vsiz < TTIOBUFSIZ) ? stack : tcmalloc(vsiz + 1); pthread_cleanup_push(free, (vbuf == stack) ? NULL : vbuf); if(ttsockrecv(sock, vbuf, vsiz) && ttsockgetc(sock) == '\r' && ttsockgetc(sock) == '\n' && !ttsockcheckend(sock)){ int len; if(mask & ((1ULL << TTSEQPUTKEEP) | (1ULL << TTSEQALLMC) | (1ULL << TTSEQALLWRITE))){ len = sprintf(stack, "CLIENT_ERROR forbidden\r\n"); ttservlog(g_serv, TTLOGINFO, "do_mc_add: forbidden"); } else if(tculogadbputkeep(ulog, sid, 0, adb, kbuf, ksiz, vbuf, vsiz)){ len = sprintf(stack, "STORED\r\n"); } else { arg->counts[TTSEQNUM*req->idx+TTSEQPUTMISS]++; len = sprintf(stack, "NOT_STORED\r\n"); } if(nr || ttsocksend(sock, stack, len)){ req->keep = true; } else { ttservlog(g_serv, TTLOGINFO, "do_mc_add: response failed"); } } else { ttservlog(g_serv, TTLOGINFO, "do_mc_add: invalid entity"); } pthread_cleanup_pop(1); } /* handle the memcached replace command */ static void do_mc_replace(TTSOCK *sock, TASKARG *arg, TTREQ *req, char **tokens, int tnum){ ttservlog(g_serv, TTLOGDEBUG, "doing mc_replace command"); arg->counts[TTSEQNUM*req->idx+TTSEQPUT]++; uint64_t mask = arg->mask; TCADB *adb = arg->adb; TCULOG *ulog = arg->ulog; uint32_t sid = arg->sid; pthread_mutex_t *rmtxs = arg->rmtxs; if(tnum < 5){ ttsockprintf(sock, "CLIENT_ERROR error\r\n"); return; } bool nr = tnum > 5 && !strcmp(tokens[5], "noreply"); const char *kbuf = tokens[1]; int ksiz = strlen(kbuf); int vsiz = tclmax(tcatoi(tokens[4]), 0); int mtxidx = recmtxidx(kbuf, ksiz); char stack[TTIOBUFSIZ]; char *vbuf = (vsiz < TTIOBUFSIZ) ? stack : tcmalloc(vsiz + 1); pthread_cleanup_push(free, (vbuf == stack) ? NULL : vbuf); if(ttsockrecv(sock, vbuf, vsiz) && ttsockgetc(sock) == '\r' && ttsockgetc(sock) == '\n' && !ttsockcheckend(sock)){ int len; if(mask & ((1ULL << TTSEQPUT) | (1ULL << TTSEQALLMC) | (1ULL << TTSEQALLWRITE))){ len = sprintf(stack, "CLIENT_ERROR forbidden\r\n"); ttservlog(g_serv, TTLOGINFO, "do_mc_replace: forbidden"); } else { if(pthread_mutex_lock(rmtxs + mtxidx) != 0){ len = sprintf(stack, "SERVER_ERROR unexpected\r\n"); ttservlog(g_serv, TTLOGERROR, "do_mc_replace: pthread_mutex_lock failed"); } else if(tcadbvsiz(adb, kbuf, ksiz) >= 0){ if(tculogadbput(ulog, sid, 0, adb, kbuf, ksiz, vbuf, vsiz)){ len = sprintf(stack, "STORED\r\n"); } else { len = sprintf(stack, "SERVER_ERROR unexpected\r\n"); ttservlog(g_serv, TTLOGERROR, "do_mc_replace: operation failed"); } } else { arg->counts[TTSEQNUM*req->idx+TTSEQPUTMISS]++; len = sprintf(stack, "NOT_STORED\r\n"); } if(pthread_mutex_unlock(rmtxs + mtxidx) != 0) ttservlog(g_serv, TTLOGERROR, "do_mc_incr: pthread_mutex_unlock failed"); } if(nr || ttsocksend(sock, stack, len)){ req->keep = true; } else { ttservlog(g_serv, TTLOGINFO, "do_mc_replace: response failed"); } } else { ttservlog(g_serv, TTLOGINFO, "do_mc_replace: invalid entity"); } pthread_cleanup_pop(1); } /* handle the memcached append command */ static void do_mc_append(TTSOCK *sock, TASKARG *arg, TTREQ *req, char **tokens, int tnum){ ttservlog(g_serv, TTLOGDEBUG, "doing mc_append command"); arg->counts[TTSEQNUM*req->idx+TTSEQPUT]++; uint64_t mask = arg->mask; TCADB *adb = arg->adb; TCULOG *ulog = arg->ulog; uint32_t sid = arg->sid; pthread_mutex_t *rmtxs = arg->rmtxs; if(tnum < 5){ ttsockprintf(sock, "CLIENT_ERROR error\r\n"); return; } bool nr = tnum > 5 && !strcmp(tokens[5], "noreply"); const char *kbuf = tokens[1]; int ksiz = strlen(kbuf); int vsiz = tclmax(tcatoi(tokens[4]), 0); int mtxidx = recmtxidx(kbuf, ksiz); char stack[TTIOBUFSIZ]; char *vbuf = (vsiz < TTIOBUFSIZ) ? stack : tcmalloc(vsiz + 1); pthread_cleanup_push(free, (vbuf == stack) ? NULL : vbuf); if(ttsockrecv(sock, vbuf, vsiz) && ttsockgetc(sock) == '\r' && ttsockgetc(sock) == '\n' && !ttsockcheckend(sock)){ int len; if(mask & ((1ULL << TTSEQPUT) | (1ULL << TTSEQALLMC) | (1ULL << TTSEQALLWRITE))){ len = sprintf(stack, "CLIENT_ERROR forbidden\r\n"); ttservlog(g_serv, TTLOGINFO, "do_mc_append: forbidden"); } else { if(pthread_mutex_lock(rmtxs + mtxidx) != 0){ len = sprintf(stack, "SERVER_ERROR unexpected\r\n"); ttservlog(g_serv, TTLOGERROR, "do_mc_append: pthread_mutex_lock failed"); } else if(tcadbvsiz(adb, kbuf, ksiz) >= 0){ if(tculogadbputcat(ulog, sid, 0, adb, kbuf, ksiz, vbuf, vsiz)){ len = sprintf(stack, "STORED\r\n"); } else { len = sprintf(stack, "SERVER_ERROR unexpected\r\n"); ttservlog(g_serv, TTLOGERROR, "do_mc_append: operation failed"); } } else { arg->counts[TTSEQNUM*req->idx+TTSEQPUTMISS]++; len = sprintf(stack, "NOT_STORED\r\n"); } if(pthread_mutex_unlock(rmtxs + mtxidx) != 0) ttservlog(g_serv, TTLOGERROR, "do_mc_incr: pthread_mutex_unlock failed"); } if(nr || ttsocksend(sock, stack, len)){ req->keep = true; } else { ttservlog(g_serv, TTLOGINFO, "do_mc_append: response failed"); } } else { ttservlog(g_serv, TTLOGINFO, "do_mc_append: invalid entity"); } pthread_cleanup_pop(1); } /* handle the memcached prepend command */ static void do_mc_prepend(TTSOCK *sock, TASKARG *arg, TTREQ *req, char **tokens, int tnum){ ttservlog(g_serv, TTLOGDEBUG, "doing mc_prepend command"); arg->counts[TTSEQNUM*req->idx+TTSEQPUT]++; uint64_t mask = arg->mask; TCADB *adb = arg->adb; TCULOG *ulog = arg->ulog; uint32_t sid = arg->sid; pthread_mutex_t *rmtxs = arg->rmtxs; if(tnum < 5){ ttsockprintf(sock, "CLIENT_ERROR error\r\n"); return; } bool nr = tnum > 5 && !strcmp(tokens[5], "noreply"); const char *kbuf = tokens[1]; int ksiz = strlen(kbuf); int vsiz = tclmax(tcatoi(tokens[4]), 0); int mtxidx = recmtxidx(kbuf, ksiz); char stack[TTIOBUFSIZ]; char *vbuf = (vsiz < TTIOBUFSIZ) ? stack : tcmalloc(vsiz + 1); pthread_cleanup_push(free, (vbuf == stack) ? NULL : vbuf); if(ttsockrecv(sock, vbuf, vsiz) && ttsockgetc(sock) == '\r' && ttsockgetc(sock) == '\n' && !ttsockcheckend(sock)){ int len; if(mask & ((1ULL << TTSEQPUT) | (1ULL << TTSEQALLMC) | (1ULL << TTSEQALLWRITE))){ len = sprintf(stack, "CLIENT_ERROR forbidden\r\n"); ttservlog(g_serv, TTLOGINFO, "do_mc_prepend: forbidden"); } else { char *obuf; int osiz; if(pthread_mutex_lock(rmtxs + mtxidx) != 0){ len = sprintf(stack, "SERVER_ERROR unexpected\r\n"); ttservlog(g_serv, TTLOGERROR, "do_mc_prepend: pthread_mutex_lock failed"); } else if((obuf = tcadbget(adb, kbuf, ksiz, &osiz)) != NULL){ char *nbuf = tcmalloc(vsiz + osiz + 1); memcpy(nbuf, vbuf, vsiz); memcpy(nbuf + vsiz, obuf, osiz); tculogadbput(ulog, sid, 0, adb, kbuf, ksiz, nbuf, vsiz + osiz); len = sprintf(stack, "STORED\r\n"); tcfree(nbuf); tcfree(obuf); } else { arg->counts[TTSEQNUM*req->idx+TTSEQPUTMISS]++; len = sprintf(stack, "NOT_STORED\r\n"); } if(pthread_mutex_unlock(rmtxs + mtxidx) != 0) ttservlog(g_serv, TTLOGERROR, "do_mc_incr: pthread_mutex_unlock failed"); } if(nr || ttsocksend(sock, stack, len)){ req->keep = true; } else { ttservlog(g_serv, TTLOGINFO, "do_mc_prepend: response failed"); } } else { ttservlog(g_serv, TTLOGINFO, "do_mc_prepend: invalid entity"); } pthread_cleanup_pop(1); } /* handle the memcached get command */ static void do_mc_get(TTSOCK *sock, TASKARG *arg, TTREQ *req, char **tokens, int tnum){ ttservlog(g_serv, TTLOGDEBUG, "doing mc_get command"); uint64_t mask = arg->mask; TCADB *adb = arg->adb; if(tnum < 2){ ttsockprintf(sock, "CLIENT_ERROR error\r\n"); return; } TCXSTR *xstr = tcxstrnew(); pthread_cleanup_push((void (*)(void *))tcxstrdel, xstr); for(int i = 1; i < tnum; i++){ arg->counts[TTSEQNUM*req->idx+TTSEQGET]++; const char *kbuf = tokens[i]; int ksiz = strlen(kbuf); int vsiz; char *vbuf; if(mask & ((1ULL << TTSEQGET) | (1ULL << TTSEQALLMC) | (1ULL << TTSEQALLREAD))){ vbuf = NULL; vsiz = 0; ttservlog(g_serv, TTLOGINFO, "do_mc_get: forbidden"); } else { vbuf = tcadbget(adb, kbuf, ksiz, &vsiz); } if(vbuf){ tcxstrprintf(xstr, "VALUE %s 0 %d\r\n", kbuf, vsiz); tcxstrcat(xstr, vbuf, vsiz); tcxstrcat(xstr, "\r\n", 2); tcfree(vbuf); } else { arg->counts[TTSEQNUM*req->idx+TTSEQGETMISS]++; } } tcxstrprintf(xstr, "END\r\n"); if(ttsocksend(sock, tcxstrptr(xstr), tcxstrsize(xstr))){ req->keep = true; } else { ttservlog(g_serv, TTLOGINFO, "do_mc_get: response failed"); } pthread_cleanup_pop(1); } /* handle the memcached delete command */ static void do_mc_delete(TTSOCK *sock, TASKARG *arg, TTREQ *req, char **tokens, int tnum){ ttservlog(g_serv, TTLOGDEBUG, "doing mc_delete command"); arg->counts[TTSEQNUM*req->idx+TTSEQOUT]++; uint64_t mask = arg->mask; TCADB *adb = arg->adb; TCULOG *ulog = arg->ulog; uint32_t sid = arg->sid; if(tnum < 2){ ttsockprintf(sock, "CLIENT_ERROR error\r\n"); return; } bool nr = (tnum > 2 && !strcmp(tokens[2], "noreply")) || (tnum > 3 && !strcmp(tokens[3], "noreply")); const char *kbuf = tokens[1]; int ksiz = strlen(kbuf); char stack[TTIOBUFSIZ]; int len; if(mask & ((1ULL << TTSEQOUT) | (1ULL << TTSEQALLMC) | (1ULL << TTSEQALLWRITE))){ len = sprintf(stack, "CLIENT_ERROR forbidden\r\n"); ttservlog(g_serv, TTLOGINFO, "do_mc_delete: forbidden"); } else if(tculogadbout(ulog, sid, 0, adb, kbuf, ksiz)){ len = sprintf(stack, "DELETED\r\n"); } else { arg->counts[TTSEQNUM*req->idx+TTSEQOUTMISS]++; len = sprintf(stack, "NOT_FOUND\r\n"); } if(nr || ttsocksend(sock, stack, len)){ req->keep = true; } else { ttservlog(g_serv, TTLOGINFO, "do_mc_delete: response failed"); } } /* handle the memcached incr command */ static void do_mc_incr(TTSOCK *sock, TASKARG *arg, TTREQ *req, char **tokens, int tnum){ ttservlog(g_serv, TTLOGDEBUG, "doing mc_incr command"); arg->counts[TTSEQNUM*req->idx+TTSEQADDINT]++; uint64_t mask = arg->mask; TCADB *adb = arg->adb; TCULOG *ulog = arg->ulog; uint32_t sid = arg->sid; pthread_mutex_t *rmtxs = arg->rmtxs; if(tnum < 3){ ttsockprintf(sock, "CLIENT_ERROR error\r\n"); return; } bool nr = tnum > 3 && !strcmp(tokens[3], "noreply"); const char *kbuf = tokens[1]; int ksiz = strlen(kbuf); int64_t num = tcatoi(tokens[2]); int mtxidx = recmtxidx(kbuf, ksiz); char stack[TTIOBUFSIZ]; int len; if(mask & ((1ULL << TTSEQADDINT) | (1ULL << TTSEQALLMC) | (1ULL << TTSEQALLWRITE))){ len = sprintf(stack, "CLIENT_ERROR forbidden\r\n"); ttservlog(g_serv, TTLOGINFO, "do_mc_incr: forbidden"); } else { if(pthread_mutex_lock(rmtxs + mtxidx) != 0){ ttsockprintf(sock, "SERVER_ERROR unexpected\r\n"); ttservlog(g_serv, TTLOGERROR, "do_mc_incr: pthread_mutex_lock failed"); return; } int vsiz; char *vbuf = tcadbget(adb, kbuf, ksiz, &vsiz); if(vbuf){ num += tcatoi(vbuf); if(num < 0) num = 0; len = sprintf(stack, "%lld", (long long)num); if(tculogadbput(ulog, sid, 0, adb, kbuf, ksiz, stack, len)){ len = sprintf(stack, "%lld\r\n", (long long)num); } else { len = sprintf(stack, "SERVER_ERROR unexpected\r\n"); ttservlog(g_serv, TTLOGERROR, "do_mc_incr: operation failed"); } tcfree(vbuf); } else { len = sprintf(stack, "NOT_FOUND\r\n"); } if(pthread_mutex_unlock(rmtxs + mtxidx) != 0) ttservlog(g_serv, TTLOGERROR, "do_mc_incr: pthread_mutex_unlock failed"); } if(nr || ttsocksend(sock, stack, len)){ req->keep = true; } else { ttservlog(g_serv, TTLOGINFO, "do_mc_incr: response failed"); } } /* handle the memcached decr command */ static void do_mc_decr(TTSOCK *sock, TASKARG *arg, TTREQ *req, char **tokens, int tnum){ ttservlog(g_serv, TTLOGDEBUG, "doing mc_decr command"); arg->counts[TTSEQNUM*req->idx+TTSEQADDINT]++; uint64_t mask = arg->mask; TCADB *adb = arg->adb; TCULOG *ulog = arg->ulog; uint32_t sid = arg->sid; pthread_mutex_t *rmtxs = arg->rmtxs; if(tnum < 3){ ttsockprintf(sock, "CLIENT_ERROR error\r\n"); return; } bool nr = tnum > 3 && !strcmp(tokens[3], "noreply"); const char *kbuf = tokens[1]; int ksiz = strlen(kbuf); int64_t num = tcatoi(tokens[2]) * -1; int mtxidx = recmtxidx(kbuf, ksiz); char stack[TTIOBUFSIZ]; int len; if(mask & ((1ULL << TTSEQADDINT) | (1ULL << TTSEQALLMC) | (1ULL << TTSEQALLWRITE))){ len = sprintf(stack, "CLIENT_ERROR forbidden\r\n"); ttservlog(g_serv, TTLOGINFO, "do_mc_decr: forbidden"); } else { if(pthread_mutex_lock(rmtxs + mtxidx) != 0){ ttsockprintf(sock, "SERVER_ERROR unexpected\r\n"); ttservlog(g_serv, TTLOGERROR, "do_mc_decr: pthread_mutex_lock failed"); return; } int vsiz; char *vbuf = tcadbget(adb, kbuf, ksiz, &vsiz); if(vbuf){ num += tcatoi(vbuf); if(num < 0) num = 0; len = sprintf(stack, "%lld", (long long)num); if(tculogadbput(ulog, sid, 0, adb, kbuf, ksiz, stack, len)){ len = sprintf(stack, "%lld\r\n", (long long)num); } else { len = sprintf(stack, "SERVER_ERROR unexpected\r\n"); ttservlog(g_serv, TTLOGERROR, "do_mc_decr: operation failed"); } tcfree(vbuf); } else { len = sprintf(stack, "NOT_FOUND\r\n"); } if(pthread_mutex_unlock(rmtxs + mtxidx) != 0) ttservlog(g_serv, TTLOGERROR, "do_mc_decr: pthread_mutex_unlock failed"); } if(nr || ttsocksend(sock, stack, len)){ req->keep = true; } else { ttservlog(g_serv, TTLOGINFO, "do_mc_decr: response failed"); } } /* handle the memcached stat command */ static void do_mc_stats(TTSOCK *sock, TASKARG *arg, TTREQ *req, char **tokens, int tnum){ ttservlog(g_serv, TTLOGDEBUG, "doing mc_stats command"); arg->counts[TTSEQNUM*req->idx+TTSEQSTAT]++; uint64_t mask = arg->mask; TCADB *adb = arg->adb; char stack[TTIOBUFSIZ]; char *wp = stack; if(mask & ((1ULL << TTSEQSTAT) | (1ULL << TTSEQALLMC) | (1ULL << TTSEQALLREAD))){ ttservlog(g_serv, TTLOGINFO, "do_mc_stats: forbidden"); } else { wp += sprintf(wp, "STAT pid %lld\r\n", (long long)getpid()); time_t now = time(NULL); wp += sprintf(wp, "STAT uptime %lld\r\n", (long long)(now - (int)g_starttime)); wp += sprintf(wp, "STAT time %lld\r\n", (long long)now); wp += sprintf(wp, "STAT version %s\r\n", ttversion); wp += sprintf(wp, "STAT pointer_size %d\r\n", (int)sizeof(void *) * 8); struct rusage ubuf; memset(&ubuf, 0, sizeof(ubuf)); if(getrusage(RUSAGE_SELF, &ubuf) == 0){ wp += sprintf(wp, "STAT rusage_user %d.%06d\r\n", (int)ubuf.ru_utime.tv_sec, (int)ubuf.ru_utime.tv_usec); wp += sprintf(wp, "STAT rusage_system %d.%06d\r\n", (int)ubuf.ru_stime.tv_sec, (int)ubuf.ru_stime.tv_usec); } uint64_t putsum = sumstat(arg, TTSEQPUT) + sumstat(arg, TTSEQPUTKEEP) + sumstat(arg, TTSEQPUTCAT) + sumstat(arg, TTSEQPUTSHL) + sumstat(arg, TTSEQPUTNR); uint64_t putmiss = sumstat(arg, TTSEQPUTMISS); wp += sprintf(wp, "STAT cmd_set %llu\r\n", (unsigned long long)putsum); wp += sprintf(wp, "STAT cmd_set_hits %llu\r\n", (unsigned long long)(putsum - putmiss)); wp += sprintf(wp, "STAT cmd_set_misses %llu\r\n", (unsigned long long)putmiss); uint64_t outsum = sumstat(arg, TTSEQOUT); uint64_t outmiss = sumstat(arg, TTSEQOUTMISS); wp += sprintf(wp, "STAT cmd_delete %llu\r\n", (unsigned long long)outsum); wp += sprintf(wp, "STAT cmd_delete_hits %llu\r\n", (unsigned long long)(outsum - outmiss)); wp += sprintf(wp, "STAT cmd_delete_misses %llu\r\n", (unsigned long long)outmiss); uint64_t getsum = sumstat(arg, TTSEQGET); uint64_t getmiss = sumstat(arg, TTSEQGETMISS); wp += sprintf(wp, "STAT cmd_get %llu\r\n", (unsigned long long)getsum); wp += sprintf(wp, "STAT cmd_get_hits %llu\r\n", (unsigned long long)(getsum - getmiss)); wp += sprintf(wp, "STAT cmd_get_misses %llu\r\n", (unsigned long long)getmiss); wp += sprintf(wp, "STAT cmd_flush %llu\r\n", (unsigned long long)sumstat(arg, TTSEQVANISH)); int64_t rnum = tcadbrnum(adb); wp += sprintf(wp, "STAT curr_items %lld\r\n", (long long)rnum); wp += sprintf(wp, "STAT total_items %lld\r\n", (long long)rnum); wp += sprintf(wp, "STAT bytes %lld\r\n", (long long)tcadbsize(adb)); wp += sprintf(wp, "STAT threads %d\r\n", arg->thnum); wp += sprintf(wp, "END\r\n"); } if(ttsocksend(sock, stack, wp - stack)){ req->keep = true; } else { ttservlog(g_serv, TTLOGINFO, "do_mc_stats: response failed"); } } /* handle the memcached flush_all command */ static void do_mc_flushall(TTSOCK *sock, TASKARG *arg, TTREQ *req, char **tokens, int tnum){ ttservlog(g_serv, TTLOGINFO, "doing mc_flushall command"); arg->counts[TTSEQNUM*req->idx+TTSEQVANISH]++; uint64_t mask = arg->mask; TCADB *adb = arg->adb; TCULOG *ulog = arg->ulog; uint32_t sid = arg->sid; bool nr = (tnum > 1 && !strcmp(tokens[1], "noreply")) || (tnum > 2 && !strcmp(tokens[2], "noreply")); char stack[TTIOBUFSIZ]; int len; if(mask & ((1ULL << TTSEQVANISH) | (1ULL << TTSEQALLMC) | (1ULL << TTSEQALLWRITE))){ len = sprintf(stack, "CLIENT_ERROR forbidden\r\n"); ttservlog(g_serv, TTLOGINFO, "do_mc_flushall: forbidden"); } else if(tculogadbvanish(ulog, sid, 0, adb)){ len = sprintf(stack, "OK\r\n"); } else { len = sprintf(stack, "SERVER_ERROR unexpected\r\n"); ttservlog(g_serv, TTLOGERROR, "do_mc_flushall: operation failed"); } if(nr || ttsocksend(sock, stack, len)){ req->keep = true; } else { ttservlog(g_serv, TTLOGINFO, "do_mc_flushall: response failed"); } } /* handle the memcached version command */ static void do_mc_version(TTSOCK *sock, TASKARG *arg, TTREQ *req, char **tokens, int tnum){ ttservlog(g_serv, TTLOGDEBUG, "doing mc_version command"); arg->counts[TTSEQNUM*req->idx+TTSEQSTAT]++; uint64_t mask = arg->mask; char stack[TTIOBUFSIZ]; int len; if(mask & ((1ULL << TTSEQSTAT) | (1ULL << TTSEQALLMC) | (1ULL << TTSEQALLREAD))){ len = sprintf(stack, "CLIENT_ERROR forbidden\r\n"); ttservlog(g_serv, TTLOGINFO, "do_mc_version: forbidden"); } else { len = sprintf(stack, "VERSION %s\r\n", ttversion); } if(ttsocksend(sock, stack, len)){ req->keep = true; } else { ttservlog(g_serv, TTLOGINFO, "do_mc_version: response failed"); } } /* handle the memcached quit command */ static void do_mc_quit(TTSOCK *sock, TASKARG *arg, TTREQ *req, char **tokens, int tnum){ ttservlog(g_serv, TTLOGDEBUG, "doing mc_quit command"); } /* handle the HTTP GET command */ static void do_http_get(TTSOCK *sock, TASKARG *arg, TTREQ *req, int ver, const char *uri){ ttservlog(g_serv, TTLOGDEBUG, "doing http_get command"); arg->counts[TTSEQNUM*req->idx+TTSEQGET]++; uint64_t mask = arg->mask; TCADB *adb = arg->adb; bool keep = ver >= 1; char line[LINEBUFSIZ]; while(ttsockgets(sock, line, LINEBUFSIZ) && *line != '\0'){ char *pv = strchr(line, ':'); if(!pv) continue; *(pv++) = '\0'; while(*pv == ' ' || *pv == '\t'){ pv++; } if(!tcstricmp(line, "connection")){ if(!tcstricmp(pv, "close")){ keep = false; } else if(!tcstricmp(pv, "keep-alive")){ keep = true; } } } if(*uri == '/') uri++; int ksiz; char *kbuf = tcurldecode(uri, &ksiz); pthread_cleanup_push(free, kbuf); TCXSTR *xstr = tcxstrnew(); pthread_cleanup_push((void (*)(void *))tcxstrdel, xstr); if(mask & ((1ULL << TTSEQGET) | (1ULL << TTSEQALLHTTP) | (1ULL << TTSEQALLREAD))){ int len = sprintf(line, "Forbidden\n"); tcxstrprintf(xstr, "HTTP/1.1 403 Forbidden\r\n"); tcxstrprintf(xstr, "Content-Type: text/plain\r\n"); tcxstrprintf(xstr, "Content-Length: %d\r\n", len); tcxstrprintf(xstr, "\r\n"); tcxstrcat(xstr, line, len); ttservlog(g_serv, TTLOGINFO, "do_http_get: forbidden"); } else { int vsiz; char *vbuf = tcadbget(adb, kbuf, ksiz, &vsiz); if(vbuf){ tcxstrprintf(xstr, "HTTP/1.1 200 OK\r\n"); tcxstrprintf(xstr, "Content-Type: application/octet-stream\r\n"); tcxstrprintf(xstr, "Content-Length: %d\r\n", vsiz); tcxstrprintf(xstr, "\r\n"); tcxstrcat(xstr, vbuf, vsiz); tcfree(vbuf); } else { arg->counts[TTSEQNUM*req->idx+TTSEQGETMISS]++; int len = sprintf(line, "Not Found\n"); tcxstrprintf(xstr, "HTTP/1.1 404 Not Found\r\n"); tcxstrprintf(xstr, "Content-Type: text/plain\r\n"); tcxstrprintf(xstr, "Content-Length: %d\r\n", len); tcxstrprintf(xstr, "\r\n"); tcxstrcat(xstr, line, len); } } if(ttsocksend(sock, tcxstrptr(xstr), tcxstrsize(xstr))){ req->keep = keep; } else { ttservlog(g_serv, TTLOGINFO, "do_http_get: response failed"); } pthread_cleanup_pop(1); pthread_cleanup_pop(1); } /* handle the HTTP HEAD command */ static void do_http_head(TTSOCK *sock, TASKARG *arg, TTREQ *req, int ver, const char *uri){ ttservlog(g_serv, TTLOGDEBUG, "doing http_head command"); arg->counts[TTSEQNUM*req->idx+TTSEQVSIZ]++; uint64_t mask = arg->mask; TCADB *adb = arg->adb; bool keep = ver >= 1; char line[LINEBUFSIZ]; while(ttsockgets(sock, line, LINEBUFSIZ) && *line != '\0'){ char *pv = strchr(line, ':'); if(!pv) continue; *(pv++) = '\0'; while(*pv == ' ' || *pv == '\t'){ pv++; } if(!tcstricmp(line, "connection")){ if(!tcstricmp(pv, "close")){ keep = false; } else if(!tcstricmp(pv, "keep-alive")){ keep = true; } } } if(*uri == '/') uri++; int ksiz; char *kbuf = tcurldecode(uri, &ksiz); pthread_cleanup_push(free, kbuf); TCXSTR *xstr = tcxstrnew(); pthread_cleanup_push((void (*)(void *))tcxstrdel, xstr); if(mask & ((1ULL << TTSEQVSIZ) | (1ULL << TTSEQALLHTTP) | (1ULL << TTSEQALLREAD))){ tcxstrprintf(xstr, "HTTP/1.1 403 Forbidden\r\n"); tcxstrprintf(xstr, "\r\n"); ttservlog(g_serv, TTLOGINFO, "do_http_head: forbidden"); } else { int vsiz = tcadbvsiz(adb, kbuf, ksiz); if(vsiz >= 0){ tcxstrprintf(xstr, "HTTP/1.1 200 OK\r\n"); tcxstrprintf(xstr, "Content-Type: application/octet-stream\r\n"); tcxstrprintf(xstr, "Content-Length: %d\r\n", vsiz); tcxstrprintf(xstr, "\r\n"); } else { tcxstrprintf(xstr, "HTTP/1.1 404 Not Found\r\n"); tcxstrprintf(xstr, "\r\n"); } } if(ttsocksend(sock, tcxstrptr(xstr), tcxstrsize(xstr))){ req->keep = keep; } else { ttservlog(g_serv, TTLOGINFO, "do_http_head: response failed"); } pthread_cleanup_pop(1); pthread_cleanup_pop(1); } /* handle the HTTP PUT command */ static void do_http_put(TTSOCK *sock, TASKARG *arg, TTREQ *req, int ver, const char *uri){ ttservlog(g_serv, TTLOGDEBUG, "doing http_put command"); arg->counts[TTSEQNUM*req->idx+TTSEQPUT]++; uint64_t mask = arg->mask; TCADB *adb = arg->adb; TCULOG *ulog = arg->ulog; uint32_t sid = arg->sid; bool keep = ver >= 1; int vsiz = 0; int pdmode = 0; char line[LINEBUFSIZ]; while(ttsockgets(sock, line, LINEBUFSIZ) && *line != '\0'){ char *pv = strchr(line, ':'); if(!pv) continue; *(pv++) = '\0'; while(*pv == ' ' || *pv == '\t'){ pv++; } if(!tcstricmp(line, "connection")){ if(!tcstricmp(pv, "close")){ keep = false; } else if(!tcstricmp(pv, "keep-alive")){ keep = true; } } else if(!tcstricmp(line, "content-length")){ vsiz = tcatoi(pv); } else if(!tcstricmp(line, "x-tt-pdmode")){ pdmode = tcatoi(pv); } } if(*uri == '/') uri++; int ksiz; char *kbuf = tcurldecode(uri, &ksiz); pthread_cleanup_push(free, kbuf); char stack[TTIOBUFSIZ]; char *vbuf = (vsiz < TTIOBUFSIZ) ? stack : tcmalloc(vsiz + 1); pthread_cleanup_push(free, (vbuf == stack) ? NULL : vbuf); if(vsiz >= 0 && ttsockrecv(sock, vbuf, vsiz) && !ttsockcheckend(sock)){ TCXSTR *xstr = tcxstrnew(); pthread_cleanup_push((void (*)(void *))tcxstrdel, xstr); if(mask & ((1ULL << TTSEQPUT) | (1ULL << TTSEQALLHTTP) | (1ULL << TTSEQALLWRITE))){ int len = sprintf(line, "Forbidden\n"); tcxstrprintf(xstr, "HTTP/1.1 403 Forbidden\r\n"); tcxstrprintf(xstr, "Content-Type: text/plain\r\n"); tcxstrprintf(xstr, "Content-Length: %d\r\n", len); tcxstrprintf(xstr, "\r\n"); tcxstrcat(xstr, line, len); ttservlog(g_serv, TTLOGINFO, "do_http_put: forbidden"); } else { switch(pdmode){ case 1: if(tculogadbputkeep(ulog, sid, 0, adb, kbuf, ksiz, vbuf, vsiz)){ int len = sprintf(line, "Created\n"); tcxstrprintf(xstr, "HTTP/1.1 201 Created\r\n"); tcxstrprintf(xstr, "Content-Type: text/plain\r\n"); tcxstrprintf(xstr, "Content-Length: %d\r\n", len); tcxstrprintf(xstr, "\r\n"); tcxstrcat(xstr, line, len); } else { arg->counts[TTSEQNUM*req->idx+TTSEQPUTMISS]++; int len = sprintf(line, "Conflict\n"); tcxstrprintf(xstr, "HTTP/1.1 409 Conflict\r\n"); tcxstrprintf(xstr, "Content-Type: text/plain\r\n"); tcxstrprintf(xstr, "Content-Length: %d\r\n", len); tcxstrprintf(xstr, "\r\n"); tcxstrcat(xstr, line, len); } break; case 2: if(tculogadbputcat(ulog, sid, 0, adb, kbuf, ksiz, vbuf, vsiz)){ int len = sprintf(line, "Created\n"); tcxstrprintf(xstr, "HTTP/1.1 201 Created\r\n"); tcxstrprintf(xstr, "Content-Type: text/plain\r\n"); tcxstrprintf(xstr, "Content-Length: %d\r\n", len); tcxstrprintf(xstr, "\r\n"); tcxstrcat(xstr, line, len); } else { arg->counts[TTSEQNUM*req->idx+TTSEQPUTMISS]++; int len = sprintf(line, "Internal Server Error\n"); tcxstrprintf(xstr, "HTTP/1.1 500 Internal Server Error\r\n"); tcxstrprintf(xstr, "Content-Type: text/plain\r\n"); tcxstrprintf(xstr, "Content-Length: %d\r\n", len); tcxstrprintf(xstr, "\r\n"); tcxstrcat(xstr, line, len); ttservlog(g_serv, TTLOGERROR, "do_http_put: operation failed"); } break; default: if(tculogadbput(ulog, sid, 0, adb, kbuf, ksiz, vbuf, vsiz)){ int len = sprintf(line, "Created\n"); tcxstrprintf(xstr, "HTTP/1.1 201 Created\r\n"); tcxstrprintf(xstr, "Content-Type: text/plain\r\n"); tcxstrprintf(xstr, "Content-Length: %d\r\n", len); tcxstrprintf(xstr, "\r\n"); tcxstrcat(xstr, line, len); } else { arg->counts[TTSEQNUM*req->idx+TTSEQPUTMISS]++; int len = sprintf(line, "Internal Server Error\n"); tcxstrprintf(xstr, "HTTP/1.1 500 Internal Server Error\r\n"); tcxstrprintf(xstr, "Content-Type: text/plain\r\n"); tcxstrprintf(xstr, "Content-Length: %d\r\n", len); tcxstrprintf(xstr, "\r\n"); tcxstrcat(xstr, line, len); ttservlog(g_serv, TTLOGERROR, "do_http_put: operation failed"); } } } if(ttsocksend(sock, tcxstrptr(xstr), tcxstrsize(xstr))){ req->keep = keep; } else { ttservlog(g_serv, TTLOGINFO, "do_http_put: response failed"); } pthread_cleanup_pop(1); } else { ttservlog(g_serv, TTLOGINFO, "do_http_put: invalid entity"); } pthread_cleanup_pop(1); pthread_cleanup_pop(1); } /* handle the HTTP POST command */ static void do_http_post(TTSOCK *sock, TASKARG *arg, TTREQ *req, int ver, const char *uri){ ttservlog(g_serv, TTLOGDEBUG, "doing http_post command"); arg->counts[TTSEQNUM*req->idx+TTSEQEXT]++; uint64_t mask = arg->mask; pthread_mutex_t *rmtxs = arg->rmtxs; void *scr = arg->screxts[req->idx]; TCADB *adb = arg->adb; TCULOG *ulog = arg->ulog; uint32_t sid = arg->sid; bool keep = ver >= 1; int vsiz = 0; char ctype[LINEBUFSIZ/4+1]; *ctype = '\0'; char xname[LINEBUFSIZ/4+1]; *xname = '\0'; char mname[LINEBUFSIZ/4+1]; *mname = '\0'; int xopts = 0; int mopts = 0; char line[LINEBUFSIZ]; while(ttsockgets(sock, line, LINEBUFSIZ) && *line != '\0'){ char *pv = strchr(line, ':'); if(!pv) continue; *(pv++) = '\0'; while(*pv == ' ' || *pv == '\t'){ pv++; } if(!tcstricmp(line, "connection")){ if(!tcstricmp(pv, "close")){ keep = false; } else if(!tcstricmp(pv, "keep-alive")){ keep = true; } } else if(!tcstricmp(line, "content-length")){ vsiz = tcatoi(pv); } else if(!tcstricmp(line, "content-type")){ snprintf(ctype, sizeof(ctype), "%s", pv); ctype[sizeof(ctype)-1] = '\0'; } else if(!tcstricmp(line, "x-tt-xname")){ snprintf(xname, sizeof(xname), "%s", pv); xname[sizeof(xname)-1] = '\0'; } else if(!tcstricmp(line, "x-tt-mname")){ snprintf(mname, sizeof(mname), "%s", pv); mname[sizeof(mname)-1] = '\0'; } else if(!tcstricmp(line, "x-tt-xopts")){ xopts = tcatoi(pv); } else if(!tcstricmp(line, "x-tt-mopts")){ mopts = tcatoi(pv); } } if(*uri == '/') uri++; int ksiz; char *kbuf = tcurldecode(uri, &ksiz); pthread_cleanup_push(free, kbuf); char stack[TTIOBUFSIZ]; char *vbuf = (vsiz < TTIOBUFSIZ) ? stack : tcmalloc(vsiz + 1); pthread_cleanup_push(free, (vbuf == stack) ? NULL : vbuf); if(vsiz >= 0 && ttsockrecv(sock, vbuf, vsiz) && !ttsockcheckend(sock)){ TCXSTR *xstr = tcxstrnew(); pthread_cleanup_push((void (*)(void *))tcxstrdel, xstr); if(*xname != '\0'){ if(mask & ((1ULL << TTSEQEXT) | (1ULL << TTSEQALLHTTP))){ int len = sprintf(line, "Forbidden\n"); tcxstrprintf(xstr, "HTTP/1.1 403 Forbidden\r\n"); tcxstrprintf(xstr, "Content-Type: text/plain\r\n"); tcxstrprintf(xstr, "Content-Length: %d\r\n", len); tcxstrprintf(xstr, "\r\n"); tcxstrcat(xstr, line, len); ttservlog(g_serv, TTLOGINFO, "do_http_post: forbidden"); } else { int xsiz = 0; char *xbuf = NULL; if(scr){ if(xopts & RDBXOLCKGLB){ bool err = false; for(int i = 0; i < RECMTXNUM; i++){ if(pthread_mutex_lock(rmtxs + i) != 0){ ttservlog(g_serv, TTLOGERROR, "do_http_post: pthread_mutex_lock failed"); while(--i >= 0){ pthread_mutex_unlock(rmtxs + i); } err = true; break; } } if(!err){ xbuf = scrextcallmethod(scr, xname, kbuf, ksiz, vbuf, vsiz, &xsiz); for(int i = RECMTXNUM - 1; i >= 0; i--){ if(pthread_mutex_unlock(rmtxs + i) != 0) ttservlog(g_serv, TTLOGERROR, "do_http_post: pthread_mutex_unlock failed"); } } } else if(xopts & RDBXOLCKREC){ int mtxidx = recmtxidx(kbuf, ksiz); if(pthread_mutex_lock(rmtxs + mtxidx) == 0){ xbuf = scrextcallmethod(scr, xname, kbuf, ksiz, vbuf, vsiz, &xsiz); if(pthread_mutex_unlock(rmtxs + mtxidx) != 0) ttservlog(g_serv, TTLOGERROR, "do_http_post: pthread_mutex_unlock failed"); } else { ttservlog(g_serv, TTLOGERROR, "do_http_post: pthread_mutex_lock failed"); } } else { xbuf = scrextcallmethod(scr, xname, kbuf, ksiz, vbuf, vsiz, &xsiz); } } if(xbuf){ tcxstrprintf(xstr, "HTTP/1.1 200 OK\r\n"); tcxstrprintf(xstr, "Content-Type: application/octet-stream\r\n"); tcxstrprintf(xstr, "Content-Length: %d\r\n", xsiz); tcxstrprintf(xstr, "\r\n"); tcxstrcat(xstr, xbuf, xsiz); tcfree(xbuf); } else { int len = sprintf(line, "Internal Server Error\n"); tcxstrprintf(xstr, "HTTP/1.1 500 Internal Server Error\r\n"); tcxstrprintf(xstr, "Content-Type: text/plain\r\n"); tcxstrprintf(xstr, "Content-Length: %d\r\n", len); tcxstrprintf(xstr, "\r\n"); tcxstrcat(xstr, line, len); } } } else if(*mname != '\0'){ if(mask & ((1ULL << TTSEQMISC) | (1ULL << TTSEQALLHTTP))){ int len = sprintf(line, "Forbidden\n"); tcxstrprintf(xstr, "HTTP/1.1 403 Forbidden\r\n"); tcxstrprintf(xstr, "Content-Type: text/plain\r\n"); tcxstrprintf(xstr, "Content-Length: %d\r\n", len); tcxstrprintf(xstr, "\r\n"); tcxstrcat(xstr, line, len); ttservlog(g_serv, TTLOGINFO, "do_http_post: forbidden"); } else { TCMAP *params = tcmapnew2(1); pthread_cleanup_push((void (*)(void *))tcmapdel, params); if(vsiz > 0) tcwwwformdecode2(vbuf, vsiz, ctype, params); TCLIST *args = tcmapvals(params); pthread_cleanup_push((void (*)(void *))tclistdel, args); TCLIST *res = (mopts & RDBMONOULOG) ? tcadbmisc(adb, mname, args) : tculogadbmisc(ulog, sid, 0, adb, mname, args); if(res){ TCXSTR *rbuf = tcxstrnew(); for(int i = 0; i < tclistnum(res); i++){ int esiz; const char *ebuf = tclistval(res, i, &esiz); if(i > 0) tcxstrcat(rbuf, "&", 1); tcxstrprintf(rbuf, "res%d=", i + 1); char *enc = tcurlencode(ebuf, esiz); tcxstrcat2(rbuf, enc); tcfree(enc); } tcxstrprintf(xstr, "HTTP/1.1 200 OK\r\n"); tcxstrprintf(xstr, "Content-Type: application/x-www-form-urlencoded\r\n"); tcxstrprintf(xstr, "Content-Length: %d\r\n", tcxstrsize(rbuf)); tcxstrprintf(xstr, "\r\n"); tcxstrcat(xstr, tcxstrptr(rbuf), tcxstrsize(rbuf)); tcxstrdel(rbuf); tclistdel(res); } else { int len = sprintf(line, "Internal Server Error\n"); tcxstrprintf(xstr, "HTTP/1.1 500 Internal Server Error\r\n"); tcxstrprintf(xstr, "Content-Type: text/plain\r\n"); tcxstrprintf(xstr, "Content-Length: %d\r\n", len); tcxstrprintf(xstr, "\r\n"); tcxstrcat(xstr, line, len); } pthread_cleanup_pop(1); pthread_cleanup_pop(1); } } else { int len = sprintf(line, "Bad Request\n"); tcxstrprintf(xstr, "HTTP/1.1 400 Bad Request\r\n"); tcxstrprintf(xstr, "Content-Type: text/plain\r\n"); tcxstrprintf(xstr, "Content-Length: %d\r\n", len); tcxstrprintf(xstr, "\r\n"); tcxstrcat(xstr, line, len); ttservlog(g_serv, TTLOGINFO, "do_http_post: bad request"); } if(ttsocksend(sock, tcxstrptr(xstr), tcxstrsize(xstr))){ req->keep = keep; } else { ttservlog(g_serv, TTLOGINFO, "do_http_post: response failed"); } pthread_cleanup_pop(1); } else { ttservlog(g_serv, TTLOGINFO, "do_http_post: invalid entity"); } pthread_cleanup_pop(1); pthread_cleanup_pop(1); } /* handle the HTTP DELETE command */ static void do_http_delete(TTSOCK *sock, TASKARG *arg, TTREQ *req, int ver, const char *uri){ ttservlog(g_serv, TTLOGDEBUG, "doing http_delete command"); arg->counts[TTSEQNUM*req->idx+TTSEQOUT]++; uint64_t mask = arg->mask; TCADB *adb = arg->adb; TCULOG *ulog = arg->ulog; uint32_t sid = arg->sid; bool keep = ver >= 1; char line[LINEBUFSIZ]; while(ttsockgets(sock, line, LINEBUFSIZ) && *line != '\0'){ char *pv = strchr(line, ':'); if(!pv) continue; *(pv++) = '\0'; while(*pv == ' ' || *pv == '\t'){ pv++; } if(!tcstricmp(line, "connection")){ if(!tcstricmp(pv, "close")){ keep = false; } else if(!tcstricmp(pv, "keep-alive")){ keep = true; } } } if(*uri == '/') uri++; int ksiz; char *kbuf = tcurldecode(uri, &ksiz); pthread_cleanup_push(free, kbuf); TCXSTR *xstr = tcxstrnew(); pthread_cleanup_push((void (*)(void *))tcxstrdel, xstr); if(mask & ((1ULL << TTSEQOUT) | (1ULL << TTSEQALLHTTP) | (1ULL << TTSEQALLWRITE))){ int len = sprintf(line, "Forbidden\n"); tcxstrprintf(xstr, "HTTP/1.1 403 Forbidden\r\n"); tcxstrprintf(xstr, "Content-Type: text/plain\r\n"); tcxstrprintf(xstr, "Content-Length: %d\r\n", len); tcxstrprintf(xstr, "\r\n"); tcxstrcat(xstr, line, len); ttservlog(g_serv, TTLOGINFO, "do_http_delete: forbidden"); } else { if(tculogadbout(ulog, sid, 0, adb, kbuf, ksiz)){ int len = sprintf(line, "OK\n"); tcxstrprintf(xstr, "HTTP/1.1 200 OK\r\n"); tcxstrprintf(xstr, "Content-Type: text/plain\r\n"); tcxstrprintf(xstr, "Content-Length: %d\r\n", len); tcxstrprintf(xstr, "\r\n"); tcxstrcat(xstr, line, len); } else { arg->counts[TTSEQNUM*req->idx+TTSEQOUTMISS]++; int len = sprintf(line, "Not Found\n"); tcxstrprintf(xstr, "HTTP/1.1 404 Not Found\r\n"); tcxstrprintf(xstr, "Content-Type: text/plain\r\n"); tcxstrprintf(xstr, "Content-Length: %d\r\n", len); tcxstrprintf(xstr, "\r\n"); tcxstrcat(xstr, line, len); } } if(ttsocksend(sock, tcxstrptr(xstr), tcxstrsize(xstr))){ req->keep = keep; } else { ttservlog(g_serv, TTLOGINFO, "do_http_delete: response failed"); } pthread_cleanup_pop(1); pthread_cleanup_pop(1); } /* handle the HTTP OPTIONS command */ static void do_http_options(TTSOCK *sock, TASKARG *arg, TTREQ *req, int ver, const char *uri){ ttservlog(g_serv, TTLOGDEBUG, "doing http_options command"); arg->counts[TTSEQNUM*req->idx+TTSEQSTAT]++; uint64_t mask = arg->mask; TCADB *adb = arg->adb; REPLARG *sarg = arg->sarg; bool keep = ver >= 1; char line[LINEBUFSIZ]; while(ttsockgets(sock, line, LINEBUFSIZ) && *line != '\0'){ char *pv = strchr(line, ':'); if(!pv) continue; *(pv++) = '\0'; while(*pv == ' ' || *pv == '\t'){ pv++; } if(!tcstricmp(line, "connection")){ if(!tcstricmp(pv, "close")){ keep = false; } else if(!tcstricmp(pv, "keep-alive")){ keep = true; } } } TCXSTR *xstr = tcxstrnew(); pthread_cleanup_push((void (*)(void *))tcxstrdel, xstr); if(mask & ((1ULL << TTSEQSTAT) | (1ULL << TTSEQALLORG) | (1ULL << TTSEQALLREAD))){ int len = sprintf(line, "Forbidden\n"); tcxstrprintf(xstr, "HTTP/1.1 403 Forbidden\r\n"); tcxstrprintf(xstr, "Content-Type: text/plain\r\n"); tcxstrprintf(xstr, "Content-Length: %d\r\n", len); tcxstrprintf(xstr, "\r\n"); tcxstrcat(xstr, line, len); ttservlog(g_serv, TTLOGINFO, "do_http_options: forbidden"); } else { double now = tctime(); tcxstrprintf(xstr, "HTTP/1.1 200 OK\r\n"); tcxstrprintf(xstr, "Content-Length: 0\r\n"); tcxstrprintf(xstr, "Allow: OPTIONS"); if(!(mask & ((1ULL << TTSEQGET) | (1ULL << TTSEQALLREAD)))) tcxstrprintf(xstr, ", GET"); if(!(mask & ((1ULL << TTSEQVSIZ) | (1ULL << TTSEQALLREAD)))) tcxstrprintf(xstr, ", HEAD"); if(!(mask & ((1ULL << TTSEQPUT) | (1ULL << TTSEQALLWRITE)))) tcxstrprintf(xstr, ", PUT"); if(!(mask & ((1ULL << TTSEQEXT) | (1ULL << TTSEQALLWRITE)))) tcxstrprintf(xstr, ", POST"); if(!(mask & ((1ULL << TTSEQOUT) | (1ULL << TTSEQALLWRITE)))) tcxstrprintf(xstr, ", DELETE"); tcxstrprintf(xstr, "\r\n"); tcxstrprintf(xstr, "X-TT-VERSION: %s\r\n", ttversion); tcxstrprintf(xstr, "X-TT-LIBVER: %d\r\n", _TT_LIBVER); tcxstrprintf(xstr, "X-TT-PROTVER: %s\r\n", _TT_PROTVER); tcxstrprintf(xstr, "X-TT-OS: %s\r\n", TTSYSNAME); tcxstrprintf(xstr, "X-TT-TIME: %.6f\r\n", now); tcxstrprintf(xstr, "X-TT-PID: %lld\r\n", (long long)getpid()); tcxstrprintf(xstr, "X-TT-SID: %d\r\n", arg->sid); switch(tcadbomode(adb)){ case ADBOVOID: tcxstrprintf(xstr, "X-TT-TYPE: void\r\n"); break; case ADBOMDB: tcxstrprintf(xstr, "X-TT-TYPE: on-memory hash\r\n"); break; case ADBONDB: tcxstrprintf(xstr, "X-TT-TYPE: on-memory tree\r\n"); break; case ADBOHDB: tcxstrprintf(xstr, "X-TT-TYPE: hash\r\n"); break; case ADBOBDB: tcxstrprintf(xstr, "X-TT-TYPE: B+ tree\r\n"); break; case ADBOFDB: tcxstrprintf(xstr, "X-TT-TYPE: fixed-length\r\n"); break; case ADBOTDB: tcxstrprintf(xstr, "X-TT-TYPE: table\r\n"); break; case ADBOSKEL: tcxstrprintf(xstr, "X-TT-TYPE: skeleton\r\n"); break; } const char *path = tcadbpath(adb); if(path) tcxstrprintf(xstr, "X-TT-PATH: %s\r\n", path); tcxstrprintf(xstr, "X-TT-RNUM: %llu\r\n", (unsigned long long)tcadbrnum(adb)); tcxstrprintf(xstr, "X-TT-SIZE: %llu\r\n", (unsigned long long)tcadbsize(adb)); tcxstrprintf(xstr, "X-TT-BIGEND: %d\r\n", TTBIGEND); if(sarg->host[0] != '\0'){ tcxstrprintf(xstr, "X-TT-MHOST: %s\r\n", sarg->host); tcxstrprintf(xstr, "X-TT-MPORT: %d\r\n", sarg->port); tcxstrprintf(xstr, "X-TT-RTS: %llu\r\n", (unsigned long long)sarg->rts); double delay = now - sarg->rts / 1000000.0; tcxstrprintf(xstr, "X-TT-DELAY: %.6f\r\n", delay >= 0 ? delay : 0.0); } tcxstrprintf(xstr, "X-TT-FD: %d\r\n", sock->fd); tcxstrprintf(xstr, "X-TT-LOADAVG: %.6f\r\n", ttgetloadavg()); TCMAP *info = tcsysinfo(); if(info){ const char *vbuf = tcmapget2(info, "size"); if(vbuf) tcxstrprintf(xstr, "X-TT-MEMSIZE: %s\r\n", vbuf); vbuf = tcmapget2(info, "rss"); if(vbuf) tcxstrprintf(xstr, "X-TT-MEMRSS: %s\r\n", vbuf); tcmapdel(info); } tcxstrprintf(xstr, "X-TT-RU_REAL: %.6f\r\n", now - g_starttime); struct rusage ubuf; memset(&ubuf, 0, sizeof(ubuf)); if(getrusage(RUSAGE_SELF, &ubuf) == 0){ tcxstrprintf(xstr, "X-TT-RU_USER: %d.%06d\r\n", (int)ubuf.ru_utime.tv_sec, (int)ubuf.ru_utime.tv_usec); tcxstrprintf(xstr, "X-TT-RU_SYS: %d.%06d\r\n", (int)ubuf.ru_stime.tv_sec, (int)ubuf.ru_stime.tv_usec); } tcxstrprintf(xstr, "\r\n"); } if(ttsocksend(sock, tcxstrptr(xstr), tcxstrsize(xstr))){ req->keep = keep; } else { ttservlog(g_serv, TTLOGINFO, "do_http_options: response failed"); } pthread_cleanup_pop(1); } /* handle the termination event */ static void do_term(void *opq){ TERMARG *arg = (TERMARG *)opq; int thnum = arg->thnum; TCADB *adb = arg->adb; REPLARG *sarg = arg->sarg; void **screxts = arg->screxts; EXTPCARG *pcargs = arg->pcargs; int pcnum = arg->pcnum; if(sarg->host[0] != '\0') tcsleep(REPLPERIOD * 1.2); if(g_restart) return; if(pcargs){ for(int i = 0; i < pcnum; i++){ EXTPCARG *pcarg = pcargs + i; if(!pcarg->scrext) continue; if(!scrextkill(pcarg->scrext)){ arg->err = true; ttservlog(g_serv, TTLOGERROR, "scrextkill failed"); } } } for(int i = 0; i < thnum; i++){ if(!screxts[i]) continue; if(!scrextkill(screxts[i])){ arg->err = true; ttservlog(g_serv, TTLOGERROR, "scrextkill failed"); } } ttservlog(g_serv, TTLOGSYSTEM, "closing the database"); if(!tcadbclose(adb)){ arg->err = true; ttservlog(g_serv, TTLOGERROR, "tcadbclose failed"); } } // END OF FILE tokyotyrant-1.1.40/ttskeldir.c000066400000000000000000000103651133351147200163620ustar00rootroot00000000000000/************************************************************************************************* * The skeleton database library working as a directory database * Copyright (C) 2006-2010 Mikio Hirabayashi * This file is part of Tokyo Tyrant. * Tokyo Tyrant is free software; you can redistribute it and/or modify it under the terms of * the GNU Lesser General Public License as published by the Free Software Foundation; either * version 2.1 of the License or any later version. Tokyo Tyrant 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 Lesser General Public * License for more details. * You should have received a copy of the GNU Lesser General Public License along with Tokyo * Tyrant; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA. *************************************************************************************************/ #include #include #include #include #include #include #include #include #include typedef struct { /* type of structure for a directory database */ char *name; /* name of the directory */ } TCDDB; /* private funciton prototypes */ static TCDDB *tcddbnew(void); static void tcddbdel(TCDDB *ddb); static bool tcddbopen(TCDDB *ddb, const char *name); static bool tcddbclose(TCDDB *ddb); static bool tcddbput(TCDDB *ddb, const void *kbuf, int ksiz, const void *vbuf, int vsiz); static bool tcddbout(TCDDB *ddb, const void *kbuf, int ksiz); static void *tcddbget(TCDDB *ddb, const void *kbuf, int ksiz, int *sp); static char *makepath(TCDDB *ddb, const void *kbuf, int ksiz); /************************************************************************************************* * API *************************************************************************************************/ bool initialize(ADBSKEL *skel){ skel->opq = tcddbnew(); skel->del = (void (*)(void *))tcddbdel; skel->open = (bool (*)(void *, const char *))tcddbopen; skel->close = (bool (*)(void *))tcddbclose; skel->put = (bool (*)(void *, const void *, int, const void *, int))tcddbput; skel->out = (bool (*)(void *, const void *, int))tcddbout; skel->get = (void *(*)(void *, const void *, int, int *))tcddbget; return true; } /************************************************************************************************* * private features *************************************************************************************************/ static TCDDB *tcddbnew(void){ TCDDB *ddb = tcmalloc(sizeof(*ddb)); ddb->name = NULL; return ddb; } static void tcddbdel(TCDDB *ddb){ if(ddb->name) tcddbclose(ddb); tcfree(ddb); } static bool tcddbopen(TCDDB *ddb, const char *name){ if(ddb->name) return false; struct stat sbuf; if(stat(name, &sbuf) == 0){ if(!S_ISDIR(sbuf.st_mode)) return false; } else { if(mkdir(name, 0755) != 0) return false; } ddb->name = tcstrdup(name); return true; } static bool tcddbclose(TCDDB *ddb){ if(!ddb->name) return false; tcfree(ddb->name); ddb->name = NULL; return true; } static bool tcddbput(TCDDB *ddb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){ if(!ddb->name) return false; bool err = false; char *path = makepath(ddb, kbuf, ksiz); if(!tcwritefile(path, vbuf, vsiz)) err = true; tcfree(path); return !err; } static bool tcddbout(TCDDB *ddb, const void *kbuf, int ksiz){ if(!ddb->name) return false; bool err = false; char *path = makepath(ddb, kbuf, ksiz); if(unlink(path) != 0) err = true; tcfree(path); return !err; } static void *tcddbget(TCDDB *ddb, const void *kbuf, int ksiz, int *sp){ if(!ddb->name) return false; void *vbuf; char *path = makepath(ddb, kbuf, ksiz); vbuf = tcreadfile(path, -1, sp); tcfree(path); return vbuf; } static char *makepath(TCDDB *ddb, const void *kbuf, int ksiz){ char *uenc = tcurlencode(kbuf, ksiz); char *path = tcsprintf("%s/%s", ddb->name, uenc); tcfree(uenc); return path; } // END OF FILE tokyotyrant-1.1.40/ttskelmock.c000066400000000000000000000061571133351147200165410ustar00rootroot00000000000000/************************************************************************************************* * The mock-up skeleton database library * Copyright (C) 2006-2010 Mikio Hirabayashi * This file is part of Tokyo Tyrant. * Tokyo Tyrant is free software; you can redistribute it and/or modify it under the terms of * the GNU Lesser General Public License as published by the Free Software Foundation; either * version 2.1 of the License or any later version. Tokyo Tyrant 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 Lesser General Public * License for more details. * You should have received a copy of the GNU Lesser General Public License along with Tokyo * Tyrant; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA. *************************************************************************************************/ #include #include #include #include /************************************************************************************************* * API *************************************************************************************************/ bool initialize(ADBSKEL *skel){ skel->opq = tcadbnew(); skel->del = (void (*)(void *))tcadbdel; skel->open = (bool (*)(void *, const char *))tcadbopen; skel->close = (bool (*)(void *))tcadbclose; skel->put = (bool (*)(void *, const void *, int, const void *, int))tcadbput; skel->putkeep = (bool (*)(void *, const void *, int, const void *, int))tcadbputkeep; skel->putcat = (bool (*)(void *, const void *, int, const void *, int))tcadbputcat; skel->out = (bool (*)(void *, const void *, int))tcadbout; skel->get = (void *(*)(void *, const void *, int, int *))tcadbget; skel->vsiz = (int (*)(void *, const void *, int))tcadbvsiz; skel->iterinit = (bool (*)(void *))tcadbiterinit; skel->iternext = (void *(*)(void *, int *))tcadbiternext; skel->fwmkeys = (TCLIST *(*)(void *, const void *, int, int))tcadbfwmkeys; skel->addint = (int (*)(void *, const void *, int, int))tcadbaddint; skel->adddouble = (double (*)(void *, const void *, int, double))tcadbadddouble; skel->sync = (bool (*)(void *))tcadbsync; skel->optimize = (bool (*)(void *, const char *))tcadboptimize; skel->vanish = (bool (*)(void *))tcadbvanish; skel->copy = (bool (*)(void *, const char *))tcadbcopy; skel->tranbegin = (bool (*)(void *))tcadbtranbegin; skel->trancommit = (bool (*)(void *))tcadbtrancommit; skel->tranabort = (bool (*)(void *))tcadbtranabort; skel->path = (const char *(*)(void *))tcadbpath; skel->rnum = (uint64_t (*)(void *))tcadbrnum; skel->size = (uint64_t (*)(void *))tcadbsize; skel->misc = (TCLIST *(*)(void *, const char *, const TCLIST *))tcadbmisc; skel->putproc = (bool (*)(void *, const void *, int, const void *, int, TCPDPROC, void *))tcadbputproc; skel->foreach = (bool (*)(void *, TCITER, void *))tcadbforeach; return true; } // END OF FILE tokyotyrant-1.1.40/ttskelnull.c000066400000000000000000000047241133351147200165600ustar00rootroot00000000000000/************************************************************************************************* * The no-operation skeleton database library * Copyright (C) 2006-2010 Mikio Hirabayashi * This file is part of Tokyo Tyrant. * Tokyo Tyrant is free software; you can redistribute it and/or modify it under the terms of * the GNU Lesser General Public License as published by the Free Software Foundation; either * version 2.1 of the License or any later version. Tokyo Tyrant 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 Lesser General Public * License for more details. * You should have received a copy of the GNU Lesser General Public License along with Tokyo * Tyrant; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA. *************************************************************************************************/ #include #include #include #include /* private funciton prototypes */ static bool myopen(void *opq, const char *name); static bool myclose(void *opq); static bool myput(void *opq, const void *kbuf, int ksiz, const void *vbuf, int vsiz); static bool myout(void *opq, const void *kbuf, int ksiz); static TCLIST *mymisc(void *opq, const char *name, const TCLIST *args); /************************************************************************************************* * API *************************************************************************************************/ bool initialize(ADBSKEL *skel){ skel->open = myopen; skel->close = myclose; skel->put = myput; skel->out = myout; skel->misc = mymisc; return true; } /************************************************************************************************* * private features *************************************************************************************************/ static bool myopen(void *opq, const char *name){ return true; } static bool myclose(void *opq){ return true; } static bool myput(void *opq, const void *kbuf, int ksiz, const void *vbuf, int vsiz){ return true; } static bool myout(void *opq, const void *kbuf, int ksiz){ return true; } static TCLIST *mymisc(void *opq, const char *name, const TCLIST *args){ return tclistnew2(1); } // END OF FILE tokyotyrant-1.1.40/ttskelproxy.c000066400000000000000000000063671133351147200167740ustar00rootroot00000000000000/************************************************************************************************* * The skeleton database library working as a proxy * Copyright (C) 2006-2010 Mikio Hirabayashi * This file is part of Tokyo Tyrant. * Tokyo Tyrant is free software; you can redistribute it and/or modify it under the terms of * the GNU Lesser General Public License as published by the Free Software Foundation; either * version 2.1 of the License or any later version. Tokyo Tyrant 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 Lesser General Public * License for more details. * You should have received a copy of the GNU Lesser General Public License along with Tokyo * Tyrant; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA. *************************************************************************************************/ #include #include #include #include #include /* private funciton prototypes */ static TCLIST *mymisc(TCRDB *rdb, const char *name, const TCLIST *args); /************************************************************************************************* * API *************************************************************************************************/ bool initialize(ADBSKEL *skel){ skel->opq = tcrdbnew(); skel->del = (void (*)(void *))tcrdbdel; skel->open = (bool (*)(void *, const char *))tcrdbopen2; skel->close = (bool (*)(void *))tcrdbclose; skel->put = (bool (*)(void *, const void *, int, const void *, int))tcrdbput; skel->putkeep = (bool (*)(void *, const void *, int, const void *, int))tcrdbputkeep; skel->putcat = (bool (*)(void *, const void *, int, const void *, int))tcrdbputcat; skel->out = (bool (*)(void *, const void *, int))tcrdbout; skel->get = (void *(*)(void *, const void *, int, int *))tcrdbget; skel->vsiz = (int (*)(void *, const void *, int))tcrdbvsiz; skel->iterinit = (bool (*)(void *))tcrdbiterinit; skel->iternext = (void *(*)(void *, int *))tcrdbiternext; skel->fwmkeys = (TCLIST *(*)(void *, const void *, int, int))tcrdbfwmkeys; skel->addint = (int (*)(void *, const void *, int, int))tcrdbaddint; skel->adddouble = (double (*)(void *, const void *, int, double))tcrdbadddouble; skel->sync = (bool (*)(void *))tcrdbsync; skel->optimize = (bool (*)(void *, const char *))tcrdboptimize; skel->vanish = (bool (*)(void *))tcrdbvanish; skel->copy = (bool (*)(void *, const char *))tcrdbcopy; skel->path = (const char *(*)(void *))tcrdbexpr; skel->rnum = (uint64_t (*)(void *))tcrdbrnum; skel->size = (uint64_t (*)(void *))tcrdbsize; skel->misc = (TCLIST *(*)(void *, const char *, const TCLIST *))mymisc; return true; } /************************************************************************************************* * private features *************************************************************************************************/ static TCLIST *mymisc(TCRDB *rdb, const char *name, const TCLIST *args){ return tcrdbmisc(rdb, name, 0, args); } // END OF FILE tokyotyrant-1.1.40/ttulmgr.c000066400000000000000000000150311133351147200160460ustar00rootroot00000000000000/************************************************************************************************* * The test cases of the update log API * Copyright (C) 2006-2010 Mikio Hirabayashi * This file is part of Tokyo Tyrant. * Tokyo Tyrant is free software; you can redistribute it and/or modify it under the terms of * the GNU Lesser General Public License as published by the Free Software Foundation; either * version 2.1 of the License or any later version. Tokyo Tyrant 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 Lesser General Public * License for more details. * You should have received a copy of the GNU Lesser General Public License along with Tokyo * Tyrant; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA. *************************************************************************************************/ #include #include "myconf.h" #define RECBUFSIZ 32 // buffer for records typedef struct { // type of structure for read thread TCULRD *ulrd; int id; int rnum; } TARGREAD; /* global variables */ const char *g_progname; // program name /* function prototypes */ int main(int argc, char **argv); static void usage(void); static void printerr(const char *msg); static int printhex(const char *ptr, int size); static char *mygetline(FILE *ifp); static int runexport(int argc, char **argv); static int runimport(int argc, char **argv); static int procexport(const char *upath, uint64_t ts, uint32_t sid); static int procimport(const char *upath, uint64_t lim); /* main routine */ int main(int argc, char **argv){ g_progname = argv[0]; if(argc < 2) usage(); int rv = 0; if(!strcmp(argv[1], "export")){ rv = runexport(argc, argv); } else if(!strcmp(argv[1], "import")){ rv = runimport(argc, argv); } else { usage(); } return rv; } /* print the usage and exit */ static void usage(void){ fprintf(stderr, "%s: test cases of the remote database API of Tokyo Tyrant\n", g_progname); fprintf(stderr, "\n"); fprintf(stderr, "usage:\n"); fprintf(stderr, " %s export [-ts num] [-sid num] upath\n", g_progname); fprintf(stderr, " %s import upath\n", g_progname); fprintf(stderr, "\n"); exit(1); } /* print error information */ static void printerr(const char *msg){ fprintf(stderr, "%s: error: %s\n", g_progname, msg); } /* print record data */ static int printhex(const char *ptr, int size){ int len = 0; while(size-- > 0){ if(len > 0) putchar(' '); len += printf("%02X", *(unsigned char *)ptr); ptr++; } return len; } /* read a line from a file descriptor */ static char *mygetline(FILE *ifp){ int len = 0; int blen = 1024; char *buf = tcmalloc(blen); bool end = true; int c; while((c = fgetc(ifp)) != EOF){ end = false; if(c == '\0') continue; if(blen <= len){ blen *= 2; buf = tcrealloc(buf, blen + 1); } if(c == '\n' || c == '\r') c = '\0'; buf[len++] = c; if(c == '\0') break; } if(end){ tcfree(buf); return NULL; } buf[len] = '\0'; return buf; } /* parse arguments of export command */ static int runexport(int argc, char **argv){ char *upath = NULL; uint64_t ts = 0; uint32_t sid = UINT32_MAX; for(int i = 2; i < argc; i++){ if(!upath && argv[i][0] == '-'){ if(!strcmp(argv[i], "-ts")){ if(++i >= argc) usage(); ts = ttstrtots(argv[i]); } else if(!strcmp(argv[i], "-sid")){ if(++i >= argc) usage(); sid = tcatoi(argv[i]); } else { usage(); } } else if(!upath){ upath = argv[i]; } else { usage(); } } if(!upath) usage(); int rv = procexport(upath, ts, sid); return rv; } /* parse arguments of import command */ static int runimport(int argc, char **argv){ char *upath = NULL; uint64_t lim = 0; for(int i = 2; i < argc; i++){ if(!upath && argv[i][0] == '-'){ if(!strcmp(argv[i], "-lim")){ if(++i >= argc) usage(); lim = tcatoix(argv[i]); } else { usage(); } } else if(!upath){ upath = argv[i]; } else { usage(); } } if(!upath) usage(); int rv = procimport(upath, lim); return rv; } /* perform export command */ static int procexport(const char *upath, uint64_t ts, uint32_t sid){ TCULOG *ulog = tculognew(); if(!tculogopen(ulog, upath, 0)){ printerr("tculogopen"); return 1; } bool err = false; TCULRD *ulrd = tculrdnew(ulog, ts); if(ulrd){ const char *rbuf; int rsiz; uint64_t rts; uint32_t rsid, rmid; while(!err && (rbuf = tculrdread(ulrd, &rsiz, &rts, &rsid, &rmid)) != NULL){ if(rsid == sid || rmid == sid) continue; printf("%llu\t%u:%u\t", (unsigned long long)rts, (unsigned int)rsid, (unsigned int)rmid); if(rsiz >= 2){ printf("%s\t", ttcmdidtostr(((unsigned char *)rbuf)[1])); printhex(rbuf, rsiz); putchar('\n'); } else { printf("[broken entry]\n"); } } tculrddel(ulrd); } else { printerr("tculrdnew"); err = true; } if(!tculogclose(ulog)){ printerr("tculogclose"); err = true; } tculogdel(ulog); return err ? 1 : 0; } /* perform import command */ static int procimport(const char *upath, uint64_t lim){ TCULOG *ulog = tculognew(); if(!tculogopen(ulog, upath, lim)){ printerr("tculogopen"); return 1; } bool err = false; char *line; while(!err && (line = mygetline(stdin)) != NULL){ uint64_t ts = ttstrtots(line); char *pv = strchr(line, '\t'); if(!pv || ts < 1){ tcfree(line); continue; } pv++; uint32_t sid = tcatoi(pv); char *mp = strchr(pv, ':'); pv = strchr(pv, '\t'); if(!pv){ tcfree(line); continue; } pv++; uint32_t mid = 0; if(mp && mp < pv) mid = tcatoi(mp + 1); pv = strchr(pv, '\t'); if(!pv){ tcfree(line); continue; } pv++; int osiz; unsigned char *obj = (unsigned char *)tchexdecode(pv, &osiz); if(!obj || osiz < 3 || *obj != TTMAGICNUM){ tcfree(obj); tcfree(line); continue; } if(!tculogwrite(ulog, ts, sid, mid, obj, osiz)){ printerr("tculogwrite"); err = true; } tcfree(obj); tcfree(line); } if(!tculogclose(ulog)){ printerr("tculogclose"); err = true; } tculogdel(ulog); return err ? 1 : 0; } // END OF FILE tokyotyrant-1.1.40/ttultest.c000066400000000000000000000235361133351147200162510ustar00rootroot00000000000000/************************************************************************************************* * The test cases of the update log API * Copyright (C) 2006-2010 Mikio Hirabayashi * This file is part of Tokyo Tyrant. * Tokyo Tyrant is free software; you can redistribute it and/or modify it under the terms of * the GNU Lesser General Public License as published by the Free Software Foundation; either * version 2.1 of the License or any later version. Tokyo Tyrant 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 Lesser General Public * License for more details. * You should have received a copy of the GNU Lesser General Public License along with Tokyo * Tyrant; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA. *************************************************************************************************/ #include #include "myconf.h" #define RECBUFSIZ 32 // buffer for records typedef struct { // type of structure for read thread TCULRD *ulrd; int id; int rnum; } TARGREAD; /* global variables */ const char *g_progname; // program name /* function prototypes */ int main(int argc, char **argv); static void usage(void); static void iprintf(const char *format, ...); static void eprint(TCULOG *ulog, const char *func); static int runwrite(int argc, char **argv); static int runread(int argc, char **argv); static int runthread(int argc, char **argv); static int procwrite(const char *base, int rnum, int64_t limsiz, bool as); static int procread(const char *base, uint64_t ts, bool pm); static int procthread(const char *base, int tnum, int rnum, int64_t limsiz, bool as); /* main routine */ int main(int argc, char **argv){ g_progname = argv[0]; srand((unsigned int)(tctime() * 1000) % UINT_MAX); if(argc < 2) usage(); int rv = 0; if(!strcmp(argv[1], "write")){ rv = runwrite(argc, argv); } else if(!strcmp(argv[1], "read")){ rv = runread(argc, argv); } else if(!strcmp(argv[1], "thread")){ rv = runthread(argc, argv); } else { usage(); } return rv; } /* print the usage and exit */ static void usage(void){ fprintf(stderr, "%s: test cases of the remote database API of Tokyo Tyrant\n", g_progname); fprintf(stderr, "\n"); fprintf(stderr, "usage:\n"); fprintf(stderr, " %s write [-lim num] [-as] base rnum\n", g_progname); fprintf(stderr, " %s read [-ts num] [-pm] base\n", g_progname); fprintf(stderr, " %s thread [-lim num] [-as] base tnum rnum\n", g_progname); fprintf(stderr, "\n"); exit(1); } /* print formatted information string and flush the buffer */ static void iprintf(const char *format, ...){ va_list ap; va_start(ap, format); vprintf(format, ap); fflush(stdout); va_end(ap); } /* print error message of abstract database */ static void eprint(TCULOG *ulog, const char *func){ fprintf(stderr, "%s: %s: error:\n", g_progname, func); } /* parse arguments of write command */ static int runwrite(int argc, char **argv){ char *base = NULL; char *rstr = NULL; int64_t limsiz = 0; bool as = false; for(int i = 2; i < argc; i++){ if(!base && argv[i][0] == '-'){ if(!strcmp(argv[i], "-lim")){ if(++i >= argc) usage(); limsiz = tcatoi(argv[i]); } else if(!strcmp(argv[i], "-as")){ as = true; } else { usage(); } } else if(!base){ base = argv[i]; } else if(!rstr){ rstr = argv[i]; } else { usage(); } } if(!base || !rstr) usage(); int rnum = tcatoi(rstr); if(rnum < 1) usage(); int rv = procwrite(base, rnum, limsiz, as); return rv; } /* parse arguments of read command */ static int runread(int argc, char **argv){ char *base = NULL; uint64_t ts = 0; bool pm = false; for(int i = 2; i < argc; i++){ if(!base && argv[i][0] == '-'){ if(!strcmp(argv[i], "-ts")){ if(++i >= argc) usage(); ts = ttstrtots(argv[i]); } else if(!strcmp(argv[i], "-pm")){ pm = true; } else { usage(); } } else if(!base){ base = argv[i]; } else { usage(); } } if(!base) usage(); int rv = procread(base, ts, pm); return rv; } /* parse arguments of thread command */ static int runthread(int argc, char **argv){ char *base = NULL; char *tstr = NULL; char *rstr = NULL; int64_t limsiz = 0; bool as = false; for(int i = 2; i < argc; i++){ if(!base && argv[i][0] == '-'){ if(!strcmp(argv[i], "-lim")){ if(++i >= argc) usage(); limsiz = tcatoi(argv[i]); } else if(!strcmp(argv[i], "-as")){ as = true; } else { usage(); } } else if(!base){ base = argv[i]; } else if(!tstr){ tstr = argv[i]; } else if(!rstr){ rstr = argv[i]; } else { usage(); } } if(!base || !tstr || !rstr) usage(); int tnum = tcatoi(tstr); int rnum = tcatoi(rstr); if(tnum < 1 || rnum < 1) usage(); int rv = procthread(base, tnum, rnum, limsiz, as); return rv; } /* perform write command */ static int procwrite(const char *base, int rnum, int64_t limsiz, bool as){ iprintf("\n base=%s rnum=%d limsiz=%lld as=%d\n\n", base, rnum, (long long)limsiz, as); bool err = false; double stime = tctime(); TCULOG *ulog = tculognew(); if(as && !tculogsetaio(ulog)){ eprint(ulog, "tculogsetaio"); err = true; } if(!tculogopen(ulog, base, limsiz)){ eprint(ulog, "tculogopen"); err = true; } int sid = getpid() & UINT16_MAX; for(int i = 1; i <= rnum; i++){ char buf[RECBUFSIZ]; int len = sprintf(buf, "%08d", i); if(!tculogwrite(ulog, 0, sid, sid, buf, len)){ eprint(ulog, "tculogwrite"); err = true; break; } if(rnum > 250 && i % (rnum / 250) == 0){ putchar('.'); fflush(stdout); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } if(!tculogclose(ulog)){ eprint(ulog, "tculogclose"); err = true; } tculogdel(ulog); iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } /* perform read command */ static int procread(const char *base, uint64_t ts, bool pm){ if(!pm) iprintf("\n base=%s ts=%llu pm=%d\n\n", base, (unsigned long long)ts, pm); bool err = false; double stime = tctime(); TCULOG *ulog = tculognew(); if(!tculogopen(ulog, base, 0)){ eprint(ulog, "tculogopen"); err = true; } TCULRD *ulrd = tculrdnew(ulog, ts); if(ulrd){ const char *rbuf; int rsiz; uint64_t rts; uint32_t rsid, rmid; int i = 1; while((rbuf = tculrdread(ulrd, &rsiz, &rts, &rsid, &rmid)) != NULL){ if(pm){ printf("%llu\t%u:%u\t", (unsigned long long)rts, (unsigned int)rsid, (unsigned int)rmid); for(int i = 0; i < rsiz; i++){ if(i > 0) putchar(' '); printf("%02X", ((unsigned char *)rbuf)[i]); } putchar('\n'); } else { if(i % 1000 == 0){ putchar('.'); fflush(stdout); if(i % 25000 == 0) iprintf(" (%08d)\n", i); } } i++; } if(!pm) iprintf(" (%08d)\n", i - 1); tculrddel(ulrd); } if(!tculogclose(ulog)){ eprint(ulog, "tculogclose"); err = true; } tculogdel(ulog); if(!pm){ iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); } return err ? 1 : 0; } /* thread the read function */ static void *threadread(void *targ){ TCULRD *ulrd = ((TARGREAD *)targ)->ulrd; int id = ((TARGREAD *)targ)->id; int rnum = ((TARGREAD *)targ)->rnum; const char *rbuf; int rsiz; uint64_t rts; uint32_t rsid, rmid; int i = 1; while(i <= rnum){ while((rbuf = tculrdread(ulrd, &rsiz, &rts, &rsid, &rmid)) != NULL){ if(id == 0 && rnum > 250 && i % (rnum / 250) == 0){ putchar('.'); fflush(stdout); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } i++; } tcsleep(0.01); } return NULL; } /* perform thread command */ static int procthread(const char *base, int tnum, int rnum, int64_t limsiz, bool as){ iprintf("\n base=%s tnum=%d rnum=%d limsiz=%lld as=%d\n\n", base, tnum, rnum, (long long)limsiz, as); bool err = false; double stime = tctime(); TCULOG *ulog = tculognew(); if(as && !tculogsetaio(ulog)){ eprint(ulog, "tculogsetaio"); err = true; } if(!tculogopen(ulog, base, limsiz)){ eprint(ulog, "tculogopen"); err = true; } TARGREAD targs[tnum]; pthread_t threads[tnum]; for(int i = 0; i < tnum; i++){ targs[i].ulrd = tculrdnew(ulog, 0); targs[i].id = i; targs[i].rnum = rnum; if(!targs[i].ulrd){ eprint(ulog, "tculrdnew"); targs[i].id = -1; err = true; } else if(pthread_create(threads + i, NULL, threadread, targs + i) != 0){ eprint(ulog, "pthread_create"); targs[i].id = -1; err = true; } } int sid = getpid() & UINT16_MAX; for(int i = 1; i <= rnum; i++){ char buf[RECBUFSIZ]; int len = sprintf(buf, "%08d", i); if(!tculogwrite(ulog, 0, sid, sid, buf, len)){ eprint(ulog, "tculogwrite"); err = true; break; } if(rnum > 250 && i % (rnum / 10) == 0) tcsleep(0.1); } for(int i = 0; i < tnum; i++){ if(targs[i].id == -1) continue; void *rv; if(pthread_join(threads[i], &rv) != 0){ eprint(ulog, "pthread_join"); err = true; } else if(rv){ err = true; } tculrddel(targs[i].ulrd); } if(!tculogclose(ulog)){ eprint(ulog, "tculogclose"); err = true; } tculogdel(ulog); iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } // END OF FILE tokyotyrant-1.1.40/ttutil.c000066400000000000000000001321431133351147200157010ustar00rootroot00000000000000/************************************************************************************************* * The utility API of Tokyo Tyrant * Copyright (C) 2006-2010 Mikio Hirabayashi * This file is part of Tokyo Tyrant. * Tokyo Tyrant is free software; you can redistribute it and/or modify it under the terms of * the GNU Lesser General Public License as published by the Free Software Foundation; either * version 2.1 of the License or any later version. Tokyo Tyrant 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 Lesser General Public * License for more details. * You should have received a copy of the GNU Lesser General Public License along with Tokyo * Tyrant; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA. *************************************************************************************************/ #include "ttutil.h" #include "myconf.h" /************************************************************************************************* * basic utilities *************************************************************************************************/ #define SOCKPATHBUFSIZ 108 // size of a socket path buffer #define SOCKRCVTIMEO 0.25 // timeout of the recv call of socket #define SOCKSNDTIMEO 0.25 // timeout of the send call of socket #define SOCKCNCTTIMEO 5.0 // timeout of the connect call of socket #define SOCKLINEBUFSIZ 4096 // size of a line buffer of socket #define SOCKLINEMAXSIZ (16*1024*1024) // maximum size of a line of socket #define HTTPBODYMAXSIZ (256*1024*1024) // maximum size of the entity body of HTTP #define TRILLIONNUM 1000000000000 // trillion number /* String containing the version information. */ const char *ttversion = _TT_VERSION; /* Get the primary name of the local host. */ bool ttgetlocalhostname(char *name){ assert(name); if(gethostname(name, TTADDRBUFSIZ - 1) != 0){ sprintf(name, "localhost"); return false; } return true; } /* Get the address of a host. */ bool ttgethostaddr(const char *name, char *addr){ assert(name && addr); struct addrinfo hints, *result; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = 0; hints.ai_protocol = IPPROTO_TCP; hints.ai_canonname = NULL; hints.ai_addr = NULL; hints.ai_next = NULL; if(getaddrinfo(name, NULL, &hints, &result) != 0){ *addr = '\0'; return false; } if(!result){ freeaddrinfo(result); return false; } if(result->ai_addr->sa_family != AF_INET){ freeaddrinfo(result); return false; } if(getnameinfo(result->ai_addr, result->ai_addrlen, addr, TTADDRBUFSIZ, NULL, 0, NI_NUMERICHOST) != 0){ freeaddrinfo(result); return false; } freeaddrinfo(result); return true; } /* Open a client socket of TCP/IP stream to a server. */ int ttopensock(const char *addr, int port){ assert(addr && port >= 0); struct sockaddr_in sain; memset(&sain, 0, sizeof(sain)); sain.sin_family = AF_INET; if(inet_aton(addr, &sain.sin_addr) == 0) return -1; uint16_t snum = port; sain.sin_port = htons(snum); int fd = socket(PF_INET, SOCK_STREAM, 0); if(fd == -1) return -1; int optint = 1; setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *)&optint, sizeof(optint)); struct timeval opttv; opttv.tv_sec = (int)SOCKRCVTIMEO; opttv.tv_usec = (SOCKRCVTIMEO - (int)SOCKRCVTIMEO) * 1000000; setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&opttv, sizeof(opttv)); opttv.tv_sec = (int)SOCKSNDTIMEO; opttv.tv_usec = (SOCKSNDTIMEO - (int)SOCKSNDTIMEO) * 1000000; setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (char *)&opttv, sizeof(opttv)); optint = 1; setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&optint, sizeof(optint)); double dl = tctime() + SOCKCNCTTIMEO; do { int ocs = PTHREAD_CANCEL_DISABLE; pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &ocs); int rv = connect(fd, (struct sockaddr *)&sain, sizeof(sain)); int en = errno; pthread_setcancelstate(ocs, NULL); if(rv == 0) return fd; if(en != EINTR && en != EAGAIN && en != EINPROGRESS && en != EALREADY && en != ETIMEDOUT) break; } while(tctime() <= dl); close(fd); return -1; } /* Open a client socket of UNIX domain stream to a server. */ int ttopensockunix(const char *path){ assert(path); struct sockaddr_un saun; memset(&saun, 0, sizeof(saun)); saun.sun_family = AF_UNIX; snprintf(saun.sun_path, SOCKPATHBUFSIZ, "%s", path); int fd = socket(PF_UNIX, SOCK_STREAM, 0); if(fd == -1) return -1; int optint = 1; setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *)&optint, sizeof(optint)); struct timeval opttv; opttv.tv_sec = (int)SOCKRCVTIMEO; opttv.tv_usec = (SOCKRCVTIMEO - (int)SOCKRCVTIMEO) * 1000000; setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&opttv, sizeof(opttv)); opttv.tv_sec = (int)SOCKSNDTIMEO; opttv.tv_usec = (SOCKSNDTIMEO - (int)SOCKSNDTIMEO) * 1000000; setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (char *)&opttv, sizeof(opttv)); double dl = tctime() + SOCKCNCTTIMEO; do { int ocs = PTHREAD_CANCEL_DISABLE; pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &ocs); int rv = connect(fd, (struct sockaddr *)&saun, sizeof(saun)); int en = errno; pthread_setcancelstate(ocs, NULL); if(rv == 0) return fd; if(en != EINTR && en != EAGAIN && en != EINPROGRESS && en != EALREADY && en != ETIMEDOUT) break; } while(tctime() <= dl); close(fd); return -1; } /* Open a server socket of TCP/IP stream to clients. */ int ttopenservsock(const char *addr, int port){ assert(port >= 0); struct sockaddr_in sain; memset(&sain, 0, sizeof(sain)); sain.sin_family = AF_INET; if(inet_aton(addr ? addr : "0.0.0.0", &sain.sin_addr) == 0) return -1; uint16_t snum = port; sain.sin_port = htons(snum); int fd = socket(PF_INET, SOCK_STREAM, 0); if(fd == -1) return -1; int optint = 1; if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&optint, sizeof(optint)) != 0){ close(fd); return -1; } if(bind(fd, (struct sockaddr *)&sain, sizeof(sain)) != 0 || listen(fd, SOMAXCONN) != 0){ close(fd); return -1; } return fd; } /* Open a server socket of UNIX domain stream to clients. */ int ttopenservsockunix(const char *path){ assert(path); if(*path == '\0') return -1; struct sockaddr_un saun; memset(&saun, 0, sizeof(saun)); saun.sun_family = AF_UNIX; snprintf(saun.sun_path, SOCKPATHBUFSIZ, "%s", path); int fd = socket(PF_UNIX, SOCK_STREAM, 0); if(fd == -1) return -1; if(bind(fd, (struct sockaddr *)&saun, sizeof(saun)) != 0 || listen(fd, SOMAXCONN) != 0){ close(fd); return -1; } return fd; } /* Accept a TCP/IP connection from a client. */ int ttacceptsock(int fd, char *addr, int *pp){ assert(fd >= 0); do { struct sockaddr_in sain; memset(&sain, 0, sizeof(sain)); sain.sin_family = AF_INET; socklen_t slen = sizeof(sain); int cfd = accept(fd, (struct sockaddr *)&sain, &slen); if(cfd >= 0){ int optint = 1; setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *)&optint, sizeof(optint)); struct timeval opttv; opttv.tv_sec = (int)SOCKRCVTIMEO; opttv.tv_usec = (SOCKRCVTIMEO - (int)SOCKRCVTIMEO) * 1000000; setsockopt(cfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&opttv, sizeof(opttv)); opttv.tv_sec = (int)SOCKSNDTIMEO; opttv.tv_usec = (SOCKSNDTIMEO - (int)SOCKSNDTIMEO) * 1000000; setsockopt(cfd, SOL_SOCKET, SO_SNDTIMEO, (char *)&opttv, sizeof(opttv)); optint = 1; setsockopt(cfd, IPPROTO_TCP, TCP_NODELAY, (char *)&optint, sizeof(optint)); if(addr){ if(getnameinfo((struct sockaddr *)&sain, sizeof(sain), addr, TTADDRBUFSIZ, NULL, 0, NI_NUMERICHOST) != 0) sprintf(addr, "0.0.0.0"); } if(pp) *pp = (int)ntohs(sain.sin_port); return cfd; } } while(errno == EINTR || errno == EAGAIN); return -1; } /* Accept a UNIX domain connection from a client. */ int ttacceptsockunix(int fd){ assert(fd >= 0); do { int cfd = accept(fd, NULL, NULL); if(cfd >= 0){ int optint = 1; setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *)&optint, sizeof(optint)); struct timeval opttv; opttv.tv_sec = (int)SOCKRCVTIMEO; opttv.tv_usec = (SOCKRCVTIMEO - (int)SOCKRCVTIMEO) * 1000000; setsockopt(cfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&opttv, sizeof(opttv)); opttv.tv_sec = (int)SOCKSNDTIMEO; opttv.tv_usec = (SOCKSNDTIMEO - (int)SOCKSNDTIMEO) * 1000000; setsockopt(cfd, SOL_SOCKET, SO_SNDTIMEO, (char *)&opttv, sizeof(opttv)); return cfd; } } while(errno == EINTR || errno == EAGAIN); return -1; } /* Shutdown and close a socket. */ bool ttclosesock(int fd){ assert(fd >= 0); bool err = false; if(shutdown(fd, 2) != 0 && errno != ENOTCONN && errno != ECONNRESET) err = true; if(close(fd) != 0 && errno != ENOTCONN && errno != ECONNRESET) err = true; return !err; } /* Wait an I/O event of a socket. */ bool ttwaitsock(int fd, int mode, double timeout){ assert(fd >= 0 && mode >= 0 && timeout >= 0); while(true){ fd_set set; FD_ZERO(&set); FD_SET(fd, &set); double integ, fract; fract = modf(timeout, &integ); struct timespec ts; ts.tv_sec = integ; ts.tv_nsec = fract * 1000000000.0; int rv = -1; switch(mode){ case 0: rv = pselect(fd + 1, &set, NULL, NULL, &ts, NULL); break; case 1: rv = pselect(fd + 1, NULL, &set, NULL, &ts, NULL); break; case 2: rv = pselect(fd + 1, NULL, NULL, &set, &ts, NULL); break; } if(rv > 0) return true; if(rv == 0 || errno != EINVAL) break; } return false; } /* Create a socket object. */ TTSOCK *ttsocknew(int fd){ assert(fd >= 0); TTSOCK *sock = tcmalloc(sizeof(*sock)); sock->fd = fd; sock->rp = sock->buf; sock->ep = sock->buf; sock->end = false; sock->to = 0.0; sock->dl = HUGE_VAL; return sock; } /* Delete a socket object. */ void ttsockdel(TTSOCK *sock){ assert(sock); tcfree(sock); } /* Set the lifetime of a socket object. */ void ttsocksetlife(TTSOCK *sock, double lifetime){ assert(sock && lifetime >= 0); sock->to = lifetime >= INT_MAX ? 0.0 : lifetime; sock->dl = tctime() + lifetime; } /* Send data by a socket. */ bool ttsocksend(TTSOCK *sock, const void *buf, int size){ assert(sock && buf && size >= 0); const char *rp = buf; do { int ocs = PTHREAD_CANCEL_DISABLE; pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &ocs); if(sock->to > 0.0 && !ttwaitsock(sock->fd, 1, sock->to)){ pthread_setcancelstate(ocs, NULL); return false; } int wb = send(sock->fd, rp, size, 0); int en = errno; pthread_setcancelstate(ocs, NULL); switch(wb){ case -1: if(en != EINTR && en != EAGAIN && en != EWOULDBLOCK){ sock->end = true; return false; } if(tctime() > sock->dl){ sock->end = true; return false; } break; case 0: break; default: rp += wb; size -= wb; break; } } while(size > 0); return true; } /* Send formatted data by a socket. */ bool ttsockprintf(TTSOCK *sock, const char *format, ...){ assert(sock && format); bool err = false; TCXSTR *xstr = tcxstrnew(); pthread_cleanup_push((void (*)(void *))tcxstrdel, xstr); va_list ap; va_start(ap, format); while(*format != '\0'){ if(*format == '%'){ char cbuf[TTNUMBUFSIZ]; cbuf[0] = '%'; int cblen = 1; int lnum = 0; format++; while(strchr("0123456789 .+-hlLz", *format) && *format != '\0' && cblen < TTNUMBUFSIZ - 1){ if(*format == 'l' || *format == 'L') lnum++; cbuf[cblen++] = *(format++); } cbuf[cblen++] = *format; cbuf[cblen] = '\0'; int tlen; char *tmp, tbuf[TTNUMBUFSIZ*2]; switch(*format){ case 's': tmp = va_arg(ap, char *); if(!tmp) tmp = "(null)"; tcxstrcat2(xstr, tmp); break; case 'd': if(lnum >= 2){ tlen = sprintf(tbuf, cbuf, va_arg(ap, long long)); } else if(lnum >= 1){ tlen = sprintf(tbuf, cbuf, va_arg(ap, long)); } else { tlen = sprintf(tbuf, cbuf, va_arg(ap, int)); } tcxstrcat(xstr, tbuf, tlen); break; case 'o': case 'u': case 'x': case 'X': case 'c': if(lnum >= 2){ tlen = sprintf(tbuf, cbuf, va_arg(ap, unsigned long long)); } else if(lnum >= 1){ tlen = sprintf(tbuf, cbuf, va_arg(ap, unsigned long)); } else { tlen = sprintf(tbuf, cbuf, va_arg(ap, unsigned int)); } tcxstrcat(xstr, tbuf, tlen); break; case 'e': case 'E': case 'f': case 'g': case 'G': if(lnum >= 1){ tlen = sprintf(tbuf, cbuf, va_arg(ap, long double)); } else { tlen = sprintf(tbuf, cbuf, va_arg(ap, double)); } tcxstrcat(xstr, tbuf, tlen); break; case '@': tmp = va_arg(ap, char *); if(!tmp) tmp = "(null)"; while(*tmp){ switch(*tmp){ case '&': tcxstrcat(xstr, "&", 5); break; case '<': tcxstrcat(xstr, "<", 4); break; case '>': tcxstrcat(xstr, ">", 4); break; case '"': tcxstrcat(xstr, """, 6); break; default: if(!((*tmp >= 0 && *tmp <= 0x8) || (*tmp >= 0x0e && *tmp <= 0x1f))) tcxstrcat(xstr, tmp, 1); break; } tmp++; } break; case '?': tmp = va_arg(ap, char *); if(!tmp) tmp = "(null)"; while(*tmp){ unsigned char c = *(unsigned char *)tmp; if((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || (c != '\0' && strchr("_-.", c))){ tcxstrcat(xstr, tmp, 1); } else { tlen = sprintf(tbuf, "%%%02X", c); tcxstrcat(xstr, tbuf, tlen); } tmp++; } break; case '%': tcxstrcat(xstr, "%", 1); break; } } else { tcxstrcat(xstr, format, 1); } format++; } va_end(ap); if(!ttsocksend(sock, tcxstrptr(xstr), tcxstrsize(xstr))) err = true; pthread_cleanup_pop(1); return !err; } /* Receive data by a socket. */ bool ttsockrecv(TTSOCK *sock, char *buf, int size){ assert(sock && buf && size >= 0); if(sock->rp + size <= sock->ep){ memcpy(buf, sock->rp, size); sock->rp += size; return true; } bool err = false; char *wp = buf; while(size > 0){ int c = ttsockgetc(sock); if(c == -1){ err = true; break; } *(wp++) = c; size--; } return !err; } /* Receive one byte by a socket. */ int ttsockgetc(TTSOCK *sock){ assert(sock); if(sock->rp < sock->ep) return *(unsigned char *)(sock->rp++); int en; do { int ocs = PTHREAD_CANCEL_DISABLE; pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &ocs); if(sock->to > 0.0 && !ttwaitsock(sock->fd, 0, sock->to)){ pthread_setcancelstate(ocs, NULL); return -1; } int rv = recv(sock->fd, sock->buf, TTIOBUFSIZ, 0); en = errno; pthread_setcancelstate(ocs, NULL); if(rv > 0){ sock->rp = sock->buf + 1; sock->ep = sock->buf + rv; return *(unsigned char *)sock->buf; } else if(rv == 0){ sock->end = true; return -1; } } while((en == EINTR || en == EAGAIN || en == EWOULDBLOCK) && tctime() <= sock->dl); sock->end = true; return -1; } /* Push a character back to a socket. */ void ttsockungetc(TTSOCK *sock, int c){ assert(sock); if(sock->rp <= sock->buf) return; sock->rp--; *(unsigned char *)sock->rp = c; } /* Receive one line by a socket. */ bool ttsockgets(TTSOCK *sock, char *buf, int size){ assert(sock && buf && size > 0); bool err = false; size--; char *wp = buf; while(size > 0){ int c = ttsockgetc(sock); if(c == '\n') break; if(c == -1){ err = true; break; } if(c != '\r'){ *(wp++) = c; size--; } } *wp = '\0'; return !err; } /* Receive one line by a socket into allocated buffer. */ char *ttsockgets2(TTSOCK *sock){ assert(sock); bool err = false; TCXSTR *xstr = tcxstrnew3(SOCKLINEBUFSIZ); pthread_cleanup_push((void (*)(void *))tcxstrdel, xstr); int size = 0; while(true){ int c = ttsockgetc(sock); if(c == '\n') break; if(c == -1){ err = true; break; } if(c != '\r'){ unsigned char b = c; tcxstrcat(xstr, &b, sizeof(b)); size++; if(size >= SOCKLINEMAXSIZ){ err = true; break; } } } pthread_cleanup_pop(0); return tcxstrtomalloc(xstr); } /* Receive an 32-bit integer by a socket. */ uint32_t ttsockgetint32(TTSOCK *sock){ assert(sock); uint32_t num; ttsockrecv(sock, (char *)&num, sizeof(num)); return TTNTOHL(num); } /* Receive an 64-bit integer by a socket. */ uint64_t ttsockgetint64(TTSOCK *sock){ assert(sock); uint64_t num; ttsockrecv(sock, (char *)&num, sizeof(num)); return TTNTOHLL(num); } /* Check whehter a socket is end. */ bool ttsockcheckend(TTSOCK *sock){ assert(sock); return sock->end; } /* Check the size of prefetched data in a socket. */ int ttsockcheckpfsiz(TTSOCK *sock){ assert(sock); if(sock->end) return 0; return sock->ep - sock->rp; } /* Fetch the resource of a URL by HTTP. */ int tthttpfetch(const char *url, TCMAP *reqheads, TCMAP *resheads, TCXSTR *resbody){ assert(url); int code = -1; TCMAP *elems = tcurlbreak(url); pthread_cleanup_push((void (*)(void *))tcmapdel, elems); const char *scheme = tcmapget2(elems, "scheme"); const char *host = tcmapget2(elems, "host"); const char *port = tcmapget2(elems, "port"); const char *authority = tcmapget2(elems, "authority"); const char *path = tcmapget2(elems, "path"); const char *query = tcmapget2(elems, "query"); if(scheme && !tcstricmp(scheme, "http") && host){ if(*host == '\0') host = "127.0.0.1"; int pnum = port ? tcatoi(port) : 80; if(pnum < 1) pnum = 80; if(!path) path = "/"; char addr[TTADDRBUFSIZ]; int fd; if(ttgethostaddr(host, addr) && (fd = ttopensock(addr, pnum)) != -1){ pthread_cleanup_push((void (*)(void *))ttclosesock, (void *)(intptr_t)fd); TTSOCK *sock = ttsocknew(fd); pthread_cleanup_push((void (*)(void *))ttsockdel, sock); TCXSTR *obuf = tcxstrnew(); pthread_cleanup_push((void (*)(void *))tcxstrdel, obuf); if(query){ tcxstrprintf(obuf, "GET %s?%s HTTP/1.1\r\n", path, query); } else { tcxstrprintf(obuf, "GET %s HTTP/1.1\r\n", path); } if(pnum == 80){ tcxstrprintf(obuf, "Host: %s\r\n", host); } else { tcxstrprintf(obuf, "Host: %s:%d\r\n", host, pnum); } tcxstrprintf(obuf, "Connection: close\r\n", host, port); if(authority){ char *enc = tcbaseencode(authority, strlen(authority)); tcxstrprintf(obuf, "Authorization: Basic %s\r\n", enc); tcfree(enc); } double tout = -1; if(reqheads){ tcmapiterinit(reqheads); const char *name; while((name = tcmapiternext2(reqheads)) != NULL){ if(strchr(name, ':') || !tcstricmp(name, "connection")) continue; if(!tcstricmp(name, "x-tt-timeout")){ tout = tcatof(tcmapget2(reqheads, name)); } else { char *cap = tcstrdup(name); tcstrtolower(cap); char *wp = cap; bool head = true; while(*wp != '\0'){ if(head && *wp >= 'a' && *wp <= 'z') *wp -= 'a' - 'A'; head = *wp == '-' || *wp == ' '; wp++; } tcxstrprintf(obuf, "%s: %s\r\n", cap, tcmapget2(reqheads, name)); tcfree(cap); } } } tcxstrprintf(obuf, "\r\n", host); if(tout > 0) ttsocksetlife(sock, tout); if(ttsocksend(sock, tcxstrptr(obuf), tcxstrsize(obuf))){ char line[SOCKLINEBUFSIZ]; if(ttsockgets(sock, line, SOCKLINEBUFSIZ) && tcstrfwm(line, "HTTP/")){ tcstrsqzspc(line); const char *rp = strchr(line, ' '); code = tcatoi(rp + 1); if(resheads) tcmapput2(resheads, "STATUS", line); } if(code > 0){ int clen = 0; bool chunked = false; while(ttsockgets(sock, line, SOCKLINEBUFSIZ) && *line != '\0'){ tcstrsqzspc(line); char *pv = strchr(line, ':'); if(!pv) continue; *(pv++) = '\0'; while(*pv == ' '){ pv++; } tcstrtolower(line); if(!strcmp(line, "content-length")){ clen = tcatoi(pv); } else if(!strcmp(line, "transfer-encoding")){ if(!tcstricmp(pv, "chunked")) chunked = true; } if(resheads) tcmapput2(resheads, line, pv); } if(!ttsockcheckend(sock) && resbody){ bool err = false; char *body; int bsiz; if(code == 304){ body = tcmemdup("", 0); bsiz = 0; } else if(chunked){ int asiz = SOCKLINEBUFSIZ; body = tcmalloc(asiz); bsiz = 0; while(true){ pthread_cleanup_push(free, body); if(!ttsockgets(sock, line, SOCKLINEBUFSIZ)) err = true; pthread_cleanup_pop(0); if(err || *line == '\0') break; int size = tcatoih(line); if(bsiz + size > HTTPBODYMAXSIZ){ err = true; break; } if(bsiz + size > asiz){ asiz = bsiz * 2 + size; body = tcrealloc(body, asiz); } pthread_cleanup_push(free, body); if(size > 0) ttsockrecv(sock, body + bsiz, size); if(ttsockgetc(sock) != '\r' || ttsockgetc(sock) != '\n') err = true; pthread_cleanup_pop(0); if(err || size < 1) break; bsiz += size; } } else if(clen > 0){ if(clen > HTTPBODYMAXSIZ){ body = tcmemdup("", 0); bsiz = 0; err = true; } else { body = tcmalloc(clen); bsiz = 0; pthread_cleanup_push(free, body); if(ttsockrecv(sock, body, clen)){ bsiz = clen; } else { err = true; } pthread_cleanup_pop(0); } } else { int asiz = SOCKLINEBUFSIZ; body = tcmalloc(asiz); bsiz = 0; while(true){ int c; pthread_cleanup_push(free, body); c = ttsockgetc(sock); pthread_cleanup_pop(0); if(c == -1) break; if(bsiz >= HTTPBODYMAXSIZ){ err = true; break; } if(bsiz >= asiz){ asiz = bsiz * 2; body = tcrealloc(body, asiz); } body[bsiz++] = c; } } if(err){ code = -1; } else if(resbody){ tcxstrcat(resbody, body, bsiz); } tcfree(body); } } } pthread_cleanup_pop(1); pthread_cleanup_pop(1); pthread_cleanup_pop(1); } } pthread_cleanup_pop(1); return code; } /* Serialize a real number. */ void ttpackdouble(double num, char *buf){ assert(buf); double dinteg, dfract; dfract = modf(num, &dinteg); int64_t linteg, lfract; if(isnormal(dinteg) || dinteg == 0){ linteg = dinteg; lfract = dfract * TRILLIONNUM; } else if(isinf(dinteg)){ linteg = dinteg > 0 ? INT64_MAX : INT64_MIN; lfract = 0; } else { linteg = INT64_MIN; lfract = INT64_MIN; } linteg = TTHTONLL(linteg); memcpy(buf, &linteg, sizeof(linteg)); lfract = TTHTONLL(lfract); memcpy(buf + sizeof(linteg), &lfract, sizeof(lfract)); } /* Redintegrate a serialized real number. */ double ttunpackdouble(const char *buf){ assert(buf); int64_t linteg, lfract; memcpy(&linteg, buf, sizeof(linteg)); linteg = TTNTOHLL(linteg); memcpy(&lfract, buf + sizeof(linteg), sizeof(lfract)); lfract = TTNTOHLL(lfract); if(lfract == INT64_MIN && linteg == INT64_MIN){ return NAN; } else if(linteg == INT64_MAX){ return INFINITY; } else if(linteg == INT64_MIN){ return -INFINITY; } return linteg + (double)lfract / TRILLIONNUM; } /************************************************************************************************* * server utilities *************************************************************************************************/ #define TTADDRBUFSIZ 1024 // size of an address buffer #define TTDEFTHNUM 5 // default number of threads #define TTEVENTMAX 256 // maximum number of events #define TTWAITREQUEST 0.2 // waiting seconds for requests #define TTWAITWORKER 0.1 // waiting seconds for finish of workers /* private function prototypes */ static void *ttservtimer(void *argp); static void ttservtask(TTSOCK *sock, TTREQ *req); static void *ttservdeqtasks(void *argp); /* Create a server object. */ TTSERV *ttservnew(void){ TTSERV *serv = tcmalloc(sizeof(*serv)); serv->host[0] = '\0'; serv->addr[0] = '\0'; serv->port = 0; serv->queue = tclistnew(); if(pthread_mutex_init(&serv->qmtx, NULL) != 0) tcmyfatal("pthread_mutex_init failed"); if(pthread_cond_init(&serv->qcnd, NULL) != 0) tcmyfatal("pthread_cond_init failed"); if(pthread_mutex_init(&serv->tmtx, NULL) != 0) tcmyfatal("pthread_mutex_init failed"); if(pthread_cond_init(&serv->tcnd, NULL) != 0) tcmyfatal("pthread_cond_init failed"); serv->thnum = TTDEFTHNUM; serv->timeout = 0; serv->term = false; serv->do_log = NULL; serv->opq_log = NULL; serv->timernum = 0; serv->do_task = NULL; serv->opq_task = NULL; serv->do_term = NULL; serv->opq_term = NULL; return serv; } /* Delete a server object. */ void ttservdel(TTSERV *serv){ assert(serv); pthread_cond_destroy(&serv->tcnd); pthread_mutex_destroy(&serv->tmtx); pthread_cond_destroy(&serv->qcnd); pthread_mutex_destroy(&serv->qmtx); tclistdel(serv->queue); tcfree(serv); } /* Configure a server object. */ bool ttservconf(TTSERV *serv, const char *host, int port){ assert(serv); bool err = false; if(port < 1){ if(!host || host[0] == '\0'){ err = true; serv->addr[0] = '\0'; ttservlog(serv, TTLOGERROR, "invalid socket path"); } } else { if(host && !ttgethostaddr(host, serv->addr)){ err = true; serv->addr[0] = '\0'; ttservlog(serv, TTLOGERROR, "ttgethostaddr failed"); } } snprintf(serv->host, sizeof(serv->host), "%s", host ? host : ""); serv->port = port; return !err; } /* Set tuning parameters of a server object. */ void ttservtune(TTSERV *serv, int thnum, double timeout){ assert(serv && thnum > 0); serv->thnum = thnum; serv->timeout = timeout; } /* Set the logging handler of a server object. */ void ttservsetloghandler(TTSERV *serv, void (*do_log)(int, const char *, void *), void *opq){ assert(serv && do_log); serv->do_log = do_log; serv->opq_log = opq; } /* Add a timed handler to a server object. */ void ttservaddtimedhandler(TTSERV *serv, double freq, void (*do_timed)(void *), void *opq){ assert(serv && freq >= 0.0 && do_timed); if(serv->timernum >= TTTIMERMAX - 1) return; TTTIMER *timer = serv->timers + serv->timernum; timer->freq_timed = freq; timer->do_timed = do_timed; timer->opq_timed = opq; serv->timernum++; } /* Set the response handler of a server object. */ void ttservsettaskhandler(TTSERV *serv, void (*do_task)(TTSOCK *, void *, TTREQ *), void *opq){ assert(serv && do_task); serv->do_task = do_task; serv->opq_task = opq; } /* Set the termination handler of a server object. */ void ttservsettermhandler(TTSERV *serv, void (*do_term)(void *), void *opq){ assert(serv && do_term); serv->do_term = do_term; serv->opq_term = opq; } /* Start the service of a server object. */ bool ttservstart(TTSERV *serv){ assert(serv); int lfd; if(serv->port < 1){ lfd = ttopenservsockunix(serv->host); if(lfd == -1){ ttservlog(serv, TTLOGERROR, "ttopenservsockunix failed"); return false; } } else { lfd = ttopenservsock(serv->addr[0] != '\0' ? serv->addr : NULL, serv->port); if(lfd == -1){ ttservlog(serv, TTLOGERROR, "ttopenservsock failed"); return false; } } int epfd = epoll_create(TTEVENTMAX); if(epfd == -1){ close(lfd); ttservlog(serv, TTLOGERROR, "epoll_create failed"); return false; } ttservlog(serv, TTLOGSYSTEM, "service started: %d", getpid()); bool err = false; for(int i = 0; i < serv->timernum; i++){ TTTIMER *timer = serv->timers + i; timer->alive = false; timer->serv = serv; if(pthread_create(&(timer->thid), NULL, ttservtimer, timer) == 0){ ttservlog(serv, TTLOGINFO, "timer thread %d started", i + 1); timer->alive = true; } else { ttservlog(serv, TTLOGERROR, "pthread_create (ttservtimer) failed"); err = true; } } int thnum = serv->thnum; TTREQ reqs[thnum]; for(int i = 0; i < thnum; i++){ reqs[i].alive = true; reqs[i].serv = serv; reqs[i].epfd = epfd; reqs[i].mtime = tctime(); reqs[i].keep = false; reqs[i].idx = i; if(pthread_create(&reqs[i].thid, NULL, ttservdeqtasks, reqs + i) == 0){ ttservlog(serv, TTLOGINFO, "worker thread %d started", i + 1); } else { reqs[i].alive = false; err = true; ttservlog(serv, TTLOGERROR, "pthread_create (ttservdeqtasks) failed"); } } struct epoll_event ev; memset(&ev, 0, sizeof(ev)); ev.events = EPOLLIN; ev.data.fd = lfd; if(epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &ev) != 0){ err = true; ttservlog(serv, TTLOGERROR, "epoll_ctl failed"); } ttservlog(serv, TTLOGSYSTEM, "listening started"); while(!serv->term){ struct epoll_event events[TTEVENTMAX]; int fdnum = epoll_wait(epfd, events, TTEVENTMAX, TTWAITREQUEST * 1000); if(fdnum != -1){ for(int i = 0; i < fdnum; i++){ if(events[i].data.fd == lfd){ char addr[TTADDRBUFSIZ]; int port; int cfd; if(serv->port < 1){ cfd = ttacceptsockunix(lfd); sprintf(addr, "(unix)"); port = 0; } else { cfd = ttacceptsock(lfd, addr, &port); } if(epoll_reassoc(epfd, lfd) != 0){ if(cfd != -1) close(cfd); cfd = -1; } if(cfd != -1){ ttservlog(serv, TTLOGINFO, "connected: %s:%d", addr, port); struct epoll_event ev; memset(&ev, 0, sizeof(ev)); ev.events = EPOLLIN | EPOLLONESHOT; ev.data.fd = cfd; if(epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &ev) != 0){ close(cfd); err = true; ttservlog(serv, TTLOGERROR, "epoll_ctl failed"); } } else { err = true; ttservlog(serv, TTLOGERROR, "ttacceptsock failed"); if(epoll_ctl(epfd, EPOLL_CTL_DEL, lfd, NULL) != 0) ttservlog(serv, TTLOGERROR, "epoll_ctl failed"); if(close(lfd) != 0) ttservlog(serv, TTLOGERROR, "close failed"); tcsleep(TTWAITWORKER); if(serv->port < 1){ lfd = ttopenservsockunix(serv->host); if(lfd == -1) ttservlog(serv, TTLOGERROR, "ttopenservsockunix failed"); } else { lfd = ttopenservsock(serv->addr[0] != '\0' ? serv->addr : NULL, serv->port); if(lfd == -1) ttservlog(serv, TTLOGERROR, "ttopenservsock failed"); } if(lfd >= 0){ memset(&ev, 0, sizeof(ev)); ev.events = EPOLLIN; ev.data.fd = lfd; if(epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &ev) == 0){ ttservlog(serv, TTLOGSYSTEM, "listening restarted"); } else { ttservlog(serv, TTLOGERROR, "epoll_ctl failed"); } } } } else { int cfd = events[i].data.fd; if(pthread_mutex_lock(&serv->qmtx) == 0){ tclistpush(serv->queue, &cfd, sizeof(cfd)); if(pthread_mutex_unlock(&serv->qmtx) != 0){ err = true; ttservlog(serv, TTLOGERROR, "pthread_mutex_unlock failed"); } if(pthread_cond_signal(&serv->qcnd) != 0){ err = true; ttservlog(serv, TTLOGERROR, "pthread_cond_signal failed"); } } else { err = true; ttservlog(serv, TTLOGERROR, "pthread_mutex_lock failed"); } } } } else { if(errno == EINTR){ ttservlog(serv, TTLOGINFO, "signal interruption"); } else { err = true; ttservlog(serv, TTLOGERROR, "epoll_wait failed"); } } if(serv->timeout > 0){ double ctime = tctime(); for(int i = 0; i < thnum; i++){ double itime = ctime - reqs[i].mtime; if(itime > serv->timeout + TTWAITREQUEST + SOCKRCVTIMEO + SOCKSNDTIMEO && pthread_cancel(reqs[i].thid) == 0){ ttservlog(serv, TTLOGINFO, "worker thread %d canceled by timeout", i + 1); void *rv; if(pthread_join(reqs[i].thid, &rv) == 0){ if(rv && rv != PTHREAD_CANCELED) err = true; reqs[i].mtime = tctime(); if(pthread_create(&reqs[i].thid, NULL, ttservdeqtasks, reqs + i) != 0){ reqs[i].alive = false; err = true; ttservlog(serv, TTLOGERROR, "pthread_create (ttservdeqtasks) failed"); } else { ttservlog(serv, TTLOGINFO, "worker thread %d started", i + 1); } } else { reqs[i].alive = false; err = true; ttservlog(serv, TTLOGERROR, "pthread_join failed"); } } } } } ttservlog(serv, TTLOGSYSTEM, "listening finished"); if(pthread_cond_broadcast(&serv->qcnd) != 0){ err = true; ttservlog(serv, TTLOGERROR, "pthread_cond_broadcast failed"); } if(pthread_cond_broadcast(&serv->tcnd) != 0){ err = true; ttservlog(serv, TTLOGERROR, "pthread_cond_broadcast failed"); } tcsleep(TTWAITWORKER); if(serv->do_term) serv->do_term(serv->opq_term); for(int i = 0; i < thnum; i++){ if(!reqs[i].alive) continue; if(pthread_cancel(reqs[i].thid) == 0) ttservlog(serv, TTLOGINFO, "worker thread %d was canceled", i + 1); void *rv; if(pthread_join(reqs[i].thid, &rv) == 0){ ttservlog(serv, TTLOGINFO, "worker thread %d finished", i + 1); if(rv && rv != PTHREAD_CANCELED) err = true; } else { err = true; ttservlog(serv, TTLOGERROR, "pthread_join failed"); } } if(tclistnum(serv->queue) > 0) ttservlog(serv, TTLOGINFO, "%d requests discarded", tclistnum(serv->queue)); tclistclear(serv->queue); for(int i = 0; i < serv->timernum; i++){ TTTIMER *timer = serv->timers + i; if(!timer->alive) continue; void *rv; if(pthread_cancel(timer->thid) == 0) ttservlog(serv, TTLOGINFO, "timer thread %d was canceled", i + 1); if(pthread_join(timer->thid, &rv) == 0){ ttservlog(serv, TTLOGINFO, "timer thread %d finished", i + 1); if(rv && rv != PTHREAD_CANCELED) err = true; } else { err = true; ttservlog(serv, TTLOGERROR, "pthread_join failed"); } } if(epoll_close(epfd) != 0){ err = true; ttservlog(serv, TTLOGERROR, "epoll_close failed"); } if(serv->port < 1 && unlink(serv->host) == -1){ err = true; ttservlog(serv, TTLOGERROR, "unlink failed"); } if(lfd >= 0 && close(lfd) != 0){ err = true; ttservlog(serv, TTLOGERROR, "close failed"); } ttservlog(serv, TTLOGSYSTEM, "service finished"); serv->term = false; return !err; } /* Send the terminate signal to a server object. */ bool ttservkill(TTSERV *serv){ assert(serv); serv->term = true; return true; } /* Call the logging function of a server object. */ void ttservlog(TTSERV *serv, int level, const char *format, ...){ assert(serv && format); if(!serv->do_log) return; char buf[TTIOBUFSIZ]; va_list ap; va_start(ap, format); vsnprintf(buf, TTIOBUFSIZ, format, ap); va_end(ap); serv->do_log(level, buf, serv->opq_log); } /* Check whether a server object is killed. */ bool ttserviskilled(TTSERV *serv){ assert(serv); return serv->term; } /* Break a simple server expression. */ char *ttbreakservexpr(const char *expr, int *pp){ assert(expr); char *host = tcstrdup(expr); char *pv = strchr(host, '#'); if(pv) *pv = '\0'; int port = -1; pv = strchr(host, ':'); if(pv){ *(pv++) = '\0'; if(*pv >= '0' && *pv <= '9') port = tcatoi(pv); } if(port < 0) port = TTDEFPORT; if(pp) *pp = port; tcstrtrim(host); if(*host == '\0'){ tcfree(host); host = tcstrdup("127.0.0.1"); } return host; } /* Call the timed function of a server object. `argp' specifies the argument structure of the server object. The return value is `NULL' on success and other on failure. */ static void *ttservtimer(void *argp){ TTTIMER *timer = argp; TTSERV *serv = timer->serv; bool err = false; if(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL) != 0){ err = true; ttservlog(serv, TTLOGERROR, "pthread_setcancelstate failed"); } tcsleep(TTWAITWORKER); double freqi; double freqd = modf(timer->freq_timed, &freqi); while(!serv->term){ if(pthread_mutex_lock(&serv->tmtx) == 0){ struct timeval tv; struct timespec ts; if(gettimeofday(&tv, NULL) == 0){ ts.tv_sec = tv.tv_sec + (int)freqi; ts.tv_nsec = tv.tv_usec * 1000.0 + freqd * 1000000000.0; if(ts.tv_nsec >= 1000000000){ ts.tv_nsec -= 1000000000; ts.tv_sec++; } } else { ts.tv_sec = (1ULL << (sizeof(time_t) * 8 - 1)) - 1; ts.tv_nsec = 0; } int code = pthread_cond_timedwait(&serv->tcnd, &serv->tmtx, &ts); if(code == 0 || code == ETIMEDOUT || code == EINTR){ if(pthread_mutex_unlock(&serv->tmtx) != 0){ err = true; ttservlog(serv, TTLOGERROR, "pthread_mutex_unlock failed"); break; } if(code != 0 && !serv->term) timer->do_timed(timer->opq_timed); } else { pthread_mutex_unlock(&serv->tmtx); err = true; ttservlog(serv, TTLOGERROR, "pthread_cond_timedwait failed"); } } else { err = true; ttservlog(serv, TTLOGERROR, "pthread_mutex_lock failed"); } } return err ? "error" : NULL; } /* Call the task function of a server object. `req' specifies the request object. `sock' specifies the socket object. */ static void ttservtask(TTSOCK *sock, TTREQ *req){ TTSERV *serv = req->serv; if(!serv->do_task) return; serv->do_task(sock, serv->opq_task, req); } /* Dequeue tasks of a server object and dispatch them. `argp' specifies the argument structure of the server object. The return value is `NULL' on success and other on failure. */ static void *ttservdeqtasks(void *argp){ TTREQ *req = argp; TTSERV *serv = req->serv; bool err = false; if(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL) != 0){ err = true; ttservlog(serv, TTLOGERROR, "pthread_setcancelstate failed"); } sigset_t sigset; sigemptyset(&sigset); sigaddset(&sigset, SIGPIPE); sigset_t oldsigset; sigemptyset(&sigset); if(pthread_sigmask(SIG_BLOCK, &sigset, &oldsigset) != 0){ err = true; ttservlog(serv, TTLOGERROR, "pthread_sigmask failed"); } bool empty = false; while(!serv->term){ if(pthread_mutex_lock(&serv->qmtx) == 0){ struct timeval tv; struct timespec ts; if(gettimeofday(&tv, NULL) == 0){ ts.tv_sec = tv.tv_sec; ts.tv_nsec = tv.tv_usec * 1000.0 + TTWAITREQUEST * 1000000000.0; if(ts.tv_nsec >= 1000000000){ ts.tv_nsec -= 1000000000; ts.tv_sec++; } } else { ts.tv_sec = (1ULL << (sizeof(time_t) * 8 - 1)) - 1; ts.tv_nsec = 0; } int code = empty ? pthread_cond_timedwait(&serv->qcnd, &serv->qmtx, &ts) : 0; if(code == 0 || code == ETIMEDOUT || code == EINTR){ void *val = tclistshift2(serv->queue); if(pthread_mutex_unlock(&serv->qmtx) != 0){ err = true; ttservlog(serv, TTLOGERROR, "pthread_mutex_unlock failed"); } if(val){ empty = false; int cfd = *(int *)val; tcfree(val); pthread_cleanup_push((void (*)(void *))close, (void *)(intptr_t)cfd); TTSOCK *sock = ttsocknew(cfd); pthread_cleanup_push((void (*)(void *))ttsockdel, sock); bool reuse; do { if(serv->timeout > 0) ttsocksetlife(sock, serv->timeout); req->mtime = tctime(); req->keep = false; ttservtask(sock, req); reuse = false; if(sock->end){ req->keep = false; } else if(sock->ep > sock->rp){ reuse = true; } } while(reuse); pthread_cleanup_pop(1); pthread_cleanup_pop(0); if(req->keep){ struct epoll_event ev; memset(&ev, 0, sizeof(ev)); ev.events = EPOLLIN | EPOLLONESHOT; ev.data.fd = cfd; if(epoll_ctl(req->epfd, EPOLL_CTL_MOD, cfd, &ev) != 0){ close(cfd); err = true; ttservlog(serv, TTLOGERROR, "epoll_ctl failed"); } } else { if(epoll_ctl(req->epfd, EPOLL_CTL_DEL, cfd, NULL) != 0){ err = true; ttservlog(serv, TTLOGERROR, "epoll_ctl failed"); } if(!ttclosesock(cfd)){ err = true; ttservlog(serv, TTLOGERROR, "close failed"); } ttservlog(serv, TTLOGINFO, "connection finished"); } } else { empty = true; } } else { pthread_mutex_unlock(&serv->qmtx); err = true; ttservlog(serv, TTLOGERROR, "pthread_cond_timedwait failed"); } } else { err = true; ttservlog(serv, TTLOGERROR, "pthread_mutex_lock failed"); } pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); pthread_testcancel(); pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); req->mtime = tctime(); } if(pthread_sigmask(SIG_SETMASK, &oldsigset, NULL) != 0){ err = true; ttservlog(serv, TTLOGERROR, "pthread_sigmask failed"); } return err ? "error" : NULL; } /************************************************************************************************* * features for experts *************************************************************************************************/ #define NULLDEV "/dev/null" // path of the null device /* Switch the process into the background. */ bool ttdaemonize(void){ fflush(stdout); fflush(stderr); switch(fork()){ case -1: return false; case 0: break; default: _exit(0); } if(setsid() == -1) return false; switch(fork()){ case -1: return false; case 0: break; default: _exit(0); } umask(0); if(chdir(MYPATHSTR) == -1) return false; close(0); close(1); close(2); int fd = open(NULLDEV, O_RDWR, 0); if(fd != -1){ dup2(fd, 0); dup2(fd, 1); dup2(fd, 2); if(fd > 2) close(fd); } return true; } /* Get the load average of the system. */ double ttgetloadavg(void){ double avgs[3]; int anum = getloadavg(avgs, sizeof(avgs) / sizeof(*avgs)); if(anum < 1) return 0.0; return anum == 1 ? avgs[0] : avgs[1]; } /* Convert a string to a time stamp. */ uint64_t ttstrtots(const char *str){ assert(str); if(!tcstricmp(str, "now")) str = "-1"; int64_t ts = tcatoi(str); if(ts < 0) ts = tctime() * 1000000; return ts; } /* Get the command name of a command ID number. */ const char *ttcmdidtostr(int id){ switch(id){ case TTCMDPUT: return "put"; case TTCMDPUTKEEP: return "putkeep"; case TTCMDPUTCAT: return "putcat"; case TTCMDPUTSHL: return "putshl"; case TTCMDPUTNR: return "putnr"; case TTCMDOUT: return "out"; case TTCMDGET: return "get"; case TTCMDMGET: return "mget"; case TTCMDVSIZ: return "vsiz"; case TTCMDITERINIT: return "iterinit"; case TTCMDITERNEXT: return "iternext"; case TTCMDFWMKEYS: return "fwmkeys"; case TTCMDADDINT: return "addint"; case TTCMDADDDOUBLE: return "adddouble"; case TTCMDEXT: return "ext"; case TTCMDSYNC: return "sync"; case TTCMDOPTIMIZE: return "optimize"; case TTCMDVANISH: return "vanish"; case TTCMDCOPY: return "copy"; case TTCMDRESTORE: return "restore"; case TTCMDSETMST: return "setmst"; case TTCMDRNUM: return "rnum"; case TTCMDSIZE: return "size"; case TTCMDSTAT: return "stat"; case TTCMDMISC: return "misc"; case TTCMDREPL: return "repl"; } return "(unknown)"; } // END OF FILE tokyotyrant-1.1.40/ttutil.h000066400000000000000000000507761133351147200157210ustar00rootroot00000000000000/************************************************************************************************* * The utility API of Tokyo Tyrant * Copyright (C) 2006-2010 Mikio Hirabayashi * This file is part of Tokyo Tyrant. * Tokyo Tyrant is free software; you can redistribute it and/or modify it under the terms of * the GNU Lesser General Public License as published by the Free Software Foundation; either * version 2.1 of the License or any later version. Tokyo Tyrant 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 Lesser General Public * License for more details. * You should have received a copy of the GNU Lesser General Public License along with Tokyo * Tyrant; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA. *************************************************************************************************/ #ifndef _TTUTIL_H /* duplication check */ #define _TTUTIL_H #if defined(__cplusplus) #define __TTUTIL_CLINKAGEBEGIN extern "C" { #define __TTUTIL_CLINKAGEEND } #else #define __TTUTIL_CLINKAGEBEGIN #define __TTUTIL_CLINKAGEEND #endif __TTUTIL_CLINKAGEBEGIN #include #include #include #include #include #include #include /************************************************************************************************* * basic utilities *************************************************************************************************/ #define TTIOBUFSIZ 65536 /* size of an I/O buffer */ #define TTADDRBUFSIZ 1024 /* size of an address buffer */ typedef struct { /* type of structure for a socket */ int fd; /* file descriptor */ char buf[TTIOBUFSIZ]; /* reading buffer */ char *rp; /* reading pointer */ char *ep; /* end pointer */ bool end; /* end flag */ double to; /* timeout */ double dl; /* deadline time */ } TTSOCK; /* String containing the version information. */ extern const char *ttversion; /* Get the primary name of the local host. `name' specifies the pointer to the region into which the host name is written. The size of the buffer should be equal to or more than `TTADDRBUFSIZ' bytes. If successful, the return value is true, else, it is false. */ bool ttgetlocalhostname(char *name); /* Get the address of a host. `name' specifies the name of the host. `addr' specifies the pointer to the region into which the address is written. The size of the buffer should be equal to or more than `TTADDRBUFSIZ' bytes. If successful, the return value is true, else, it is false. */ bool ttgethostaddr(const char *name, char *addr); /* Open a client socket of TCP/IP stream to a server. `addr' specifies the address of the server. `port' specifies the port number of the server. The return value is the file descriptor of the stream, or -1 on error. */ int ttopensock(const char *addr, int port); /* Open a client socket of UNIX domain stream to a server. `path' specifies the path of the socket file. The return value is the file descriptor of the stream, or -1 on error. */ int ttopensockunix(const char *path); /* Open a server socket of TCP/IP stream to clients. `addr' specifies the address of the server. If it is `NULL', every network address is binded. `port' specifies the port number of the server. The return value is the file descriptor of the stream, or -1 on error. */ int ttopenservsock(const char *addr, int port); /* Open a server socket of UNIX domain stream to clients. `addr' specifies the address of the server. If it is `NULL', every network address is binded. `port' specifies the port number of the server. The return value is the file descriptor of the stream, or -1 on error. */ int ttopenservsockunix(const char *path); /* Accept a TCP/IP connection from a client. `fd' specifies the file descriptor. `addr' specifies the pointer to the region into which the client address is written. The size of the buffer should be equal to or more than `TTADDRBUFSIZ' bytes. `pp' specifies the pointer to a variable to which the client port is assigned. If it is `NULL', it is not used. The return value is the file descriptor of the stream, or -1 on error. */ int ttacceptsock(int fd, char *addr, int *pp); /* Accept a UNIX domain connection from a client. `fd' specifies the file descriptor. The return value is the file descriptor of the stream, or -1 on error. */ int ttacceptsockunix(int fd); /* Shutdown and close a socket. `fd' specifies the file descriptor. If successful, the return value is true, else, it is false. */ bool ttclosesock(int fd); /* Wait an I/O event of a socket. `fd' specifies the file descriptor. `mode' specifies the kind of events; 0 for reading, 1 for writing, 2 for exception. `timeout' specifies the timeout in seconds. If successful, the return value is true, else, it is false. */ bool ttwaitsock(int fd, int mode, double timeout); /* Create a socket object. `fd' specifies the file descriptor. The return value is the socket object. */ TTSOCK *ttsocknew(int fd); /* Delete a socket object. `sock' specifies the socket object. */ void ttsockdel(TTSOCK *sock); /* Set the lifetime of a socket object. `sock' specifies the socket object. `lifetime' specifies the lifetime seconds of each task. By default, there is no limit. */ void ttsocksetlife(TTSOCK *sock, double lifetime); /* Send data by a socket. `sock' specifies the socket object. `buf' specifies the pointer to the region of the data to send. `size' specifies the size of the buffer. If successful, the return value is true, else, it is false. */ bool ttsocksend(TTSOCK *sock, const void *buf, int size); /* Send formatted data by a socket. `sock' specifies the socket object. `format' specifies the printf-like format string. The conversion character `%' can be used with such flag characters as `s', `d', `o', `u', `x', `X', `c', `e', `E', `f', `g', `G', `@', `?', `%'. `@' works as with `s' but escapes meta characters of XML. `?' works as with `s' but escapes meta characters of URL. The other conversion character work as with each original. The other arguments are used according to the format string. If successful, the return value is true, else, it is false. */ bool ttsockprintf(TTSOCK *sock, const char *format, ...); /* Receive data by a socket. `sock' specifies the socket object. `buf' specifies the pointer to the region of the data to be received. `size' specifies the size of the buffer. If successful, the return value is true, else, it is false. False is returned if the socket is closed before receiving the specified size of data. */ bool ttsockrecv(TTSOCK *sock, char *buf, int size); /* Receive one byte by a socket. `sock' specifies the socket object. The return value is the received byte. If some error occurs or the socket is closed by the server, -1 is returned. */ int ttsockgetc(TTSOCK *sock); /* Push a character back to a socket. `sock' specifies the socket object. `c' specifies the character. */ void ttsockungetc(TTSOCK *sock, int c); /* Receive one line by a socket. `sock' specifies the socket object. `buf' specifies the pointer to the region of the data to be received. `size' specifies the size of the buffer. If successful, the return value is true, else, it is false. False is returned if the socket is closed before receiving linefeed. */ bool ttsockgets(TTSOCK *sock, char *buf, int size); /* Receive one line by a socket into allocated buffer. `sock' specifies the socket object. If successful, the return value is the pointer to the result buffer, else, it is `NULL'. `NULL' is returned if the socket is closed before receiving linefeed. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use. */ char *ttsockgets2(TTSOCK *sock); /* Receive an 32-bit integer by a socket. `sock' specifies the socket object. The return value is the 32-bit integer. */ uint32_t ttsockgetint32(TTSOCK *sock); /* Receive an 64-bit integer by a socket. `sock' specifies the socket object. The return value is the 64-bit integer. */ uint64_t ttsockgetint64(TTSOCK *sock); /* Check whehter a socket is end. `sock' specifies the socket object. The return value is true if the socket is end, else, it is false. */ bool ttsockcheckend(TTSOCK *sock); /* Check the size of prefetched data in a socket. `sock' specifies the socket object. The return value is the size of the prefetched data. */ int ttsockcheckpfsiz(TTSOCK *sock); /* Fetch the resource of a URL by HTTP. `url' specifies the URL. `reqheads' specifies a map object contains request header names and their values. The header "X-TT-Timeout" specifies the timeout in seconds. If it is `NULL', it is not used. `resheads' specifies a map object to store response headers their values. If it is NULL, it is not used. Each key of the map is an uncapitalized header name. The key "STATUS" means the status line. `resbody' specifies a extensible string object to store the entity body of the result. If it is `NULL', it is not used. The return value is the response code or -1 on network error. */ int tthttpfetch(const char *url, TCMAP *reqheads, TCMAP *resheads, TCXSTR *resbody); /* Serialize a real number. `num' specifies a real number. `buf' specifies the pointer to the region into which the result is written. The size of the buffer should be 16 bytes. */ void ttpackdouble(double num, char *buf); /* Redintegrate a serialized real number. `buf' specifies the pointer to the region of the serialized real number. The size of the buffer should be 16 bytes. The return value is the original real number. */ double ttunpackdouble(const char *buf); /************************************************************************************************* * server utilities *************************************************************************************************/ #define TTDEFPORT 1978 /* default port of the server */ #define TTMAGICNUM 0xc8 /* magic number of each command */ #define TTCMDPUT 0x10 /* ID of put command */ #define TTCMDPUTKEEP 0x11 /* ID of putkeep command */ #define TTCMDPUTCAT 0x12 /* ID of putcat command */ #define TTCMDPUTSHL 0x13 /* ID of putshl command */ #define TTCMDPUTNR 0x18 /* ID of putnr command */ #define TTCMDOUT 0x20 /* ID of out command */ #define TTCMDGET 0x30 /* ID of get command */ #define TTCMDMGET 0x31 /* ID of mget command */ #define TTCMDVSIZ 0x38 /* ID of vsiz command */ #define TTCMDITERINIT 0x50 /* ID of iterinit command */ #define TTCMDITERNEXT 0x51 /* ID of iternext command */ #define TTCMDFWMKEYS 0x58 /* ID of fwmkeys command */ #define TTCMDADDINT 0x60 /* ID of addint command */ #define TTCMDADDDOUBLE 0x61 /* ID of adddouble command */ #define TTCMDEXT 0x68 /* ID of ext command */ #define TTCMDSYNC 0x70 /* ID of sync command */ #define TTCMDOPTIMIZE 0x71 /* ID of optimize command */ #define TTCMDVANISH 0x72 /* ID of vanish command */ #define TTCMDCOPY 0x73 /* ID of copy command */ #define TTCMDRESTORE 0x74 /* ID of restore command */ #define TTCMDSETMST 0x78 /* ID of setmst command */ #define TTCMDRNUM 0x80 /* ID of rnum command */ #define TTCMDSIZE 0x81 /* ID of size command */ #define TTCMDSTAT 0x88 /* ID of stat command */ #define TTCMDMISC 0x90 /* ID of misc command */ #define TTCMDREPL 0xa0 /* ID of repl command */ #define TTTIMERMAX 8 /* maximum number of timers */ typedef struct _TTTIMER { /* type of structure for a timer */ pthread_t thid; /* thread ID */ bool alive; /* alive flag */ struct _TTSERV *serv; /* server object */ double freq_timed; /* frequency of timed handler */ void (*do_timed)(void *); /* call back function for timed handler */ void *opq_timed; /* opaque pointer for timed handler */ } TTTIMER; typedef struct _TTREQ { /* type of structure for a server */ pthread_t thid; /* thread ID */ bool alive; /* alive flag */ struct _TTSERV *serv; /* server object */ int epfd; /* polling file descriptor */ double mtime; /* last modified time */ bool keep; /* keep-alive flag */ int idx; /* ordinal index */ } TTREQ; typedef struct _TTSERV { /* type of structure for a server */ char host[TTADDRBUFSIZ]; /* host name */ char addr[TTADDRBUFSIZ]; /* host address */ uint16_t port; /* port number */ TCLIST *queue; /* queue of requests */ pthread_mutex_t qmtx; /* mutex for the queue */ pthread_cond_t qcnd; /* condition variable for the queue */ pthread_mutex_t tmtx; /* mutex for the timer */ pthread_cond_t tcnd; /* condition variable for the timer */ int thnum; /* number of threads */ double timeout; /* timeout milliseconds of each task */ bool term; /* terminate flag */ void (*do_log)(int, const char *, void *); /* call back function for logging */ void *opq_log; /* opaque pointer for logging */ TTTIMER timers[TTTIMERMAX]; /* timer objects */ int timernum; /* number of timer objects */ void (*do_task)(TTSOCK *, void *, TTREQ *); /* call back function for task */ void *opq_task; /* opaque pointer for task */ void (*do_term)(void *); /* call back gunction for termination */ void *opq_term; /* opaque pointer for termination */ } TTSERV; enum { /* enumeration for logging levels */ TTLOGDEBUG, /* debug */ TTLOGINFO, /* information */ TTLOGERROR, /* error */ TTLOGSYSTEM /* system */ }; /* Create a server object. The return value is the server object. */ TTSERV *ttservnew(void); /* Delete a server object. `serv' specifies the server object. */ void ttservdel(TTSERV *serv); /* Configure a server object. `serv' specifies the server object. `host' specifies the name or the address. If it is `NULL', If it is `NULL', every network address is binded. `port' specifies the port number. If it is not less than 0, UNIX domain socket is binded and the host name is treated as the path of the socket file. If successful, the return value is true, else, it is false. */ bool ttservconf(TTSERV *serv, const char *host, int port); /* Set tuning parameters of a server object. `serv' specifies the server object. `thnum' specifies the number of worker threads. By default, the number is 5. `timeout' specifies the timeout seconds of each task. If it is not more than 0, no timeout is specified. By default, there is no timeout. */ void ttservtune(TTSERV *serv, int thnum, double timeout); /* Set the logging handler of a server object. `serv' specifies the server object. `do_log' specifies the pointer to a function to do with a log message. Its first parameter is the log level, one of `TTLOGDEBUG', `TTLOGINFO', `TTLOGERROR'. Its second parameter is the message string. Its third parameter is the opaque pointer. `opq' specifies the opaque pointer to be passed to the handler. It can be `NULL'. */ void ttservsetloghandler(TTSERV *serv, void (*do_log)(int, const char *, void *), void *opq); /* Add a timed handler to a server object. `serv' specifies the server object. `freq' specifies the frequency of execution in seconds. `do_timed' specifies the pointer to a function to do with a event. Its parameter is the opaque pointer. `opq' specifies the opaque pointer to be passed to the handler. It can be `NULL'. */ void ttservaddtimedhandler(TTSERV *serv, double freq, void (*do_timed)(void *), void *opq); /* Set the response handler of a server object. `serv' specifies the server object. `do_task' specifies the pointer to a function to do with a task. Its first parameter is the socket object connected to the client. Its second parameter is the opaque pointer. Its third parameter is the request object. `opq' specifies the opaque pointer to be passed to the handler. It can be `NULL'. */ void ttservsettaskhandler(TTSERV *serv, void (*do_task)(TTSOCK *, void *, TTREQ *), void *opq); /* Set the termination handler of a server object. `serv' specifies the server object. `do_term' specifies the pointer to a function to do with a task. Its parameter is the opaque pointer. `opq' specifies the opaque pointer to be passed to the handler. It can be `NULL'. */ void ttservsettermhandler(TTSERV *serv, void (*do_term)(void *), void *opq); /* Start the service of a server object. `serv' specifies the server object. If successful, the return value is true, else, it is false. */ bool ttservstart(TTSERV *serv); /* Send the terminate signal to a server object. `serv' specifies the server object. If successful, the return value is true, else, it is false. */ bool ttservkill(TTSERV *serv); /* Call the logging function of a server object. `serv' specifies the server object. `level' specifies the logging level. `format' specifies the message format. The other arguments are used according to the format string. */ void ttservlog(TTSERV *serv, int level, const char *format, ...); /* Check whether a server object is killed. `serv' specifies the server object. The return value is true if the server is killed, or false if not. */ bool ttserviskilled(TTSERV *serv); /* Break a simple server expression. `expr' specifies the simple server expression. It is composed of two substrings separated by ":". The former field specifies the name or the address of the server. The latter field specifies the port number. If the latter field is omitted, the default port number is specified. `pp' specifies the pointer to a variable to which the port number is assigned. If it is `NULL', it is not used. The return value is the string of the host name. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use. */ char *ttbreakservexpr(const char *expr, int *pp); /************************************************************************************************* * features for experts *************************************************************************************************/ #define _TT_VERSION "1.1.40" #define _TT_LIBVER 323 #define _TT_PROTVER "0.91" /* Switch the process into the background. If successful, the return value is true, else, it is false. */ bool ttdaemonize(void); /* Get the load average of the system. The return value is the load average of the system. */ double ttgetloadavg(void); /* Convert a string to a time stamp. `str' specifies the string. The return value is the time stamp. */ uint64_t ttstrtots(const char *str); /* Get the command name of a command ID number. `id' specifies the command ID number. The return value is the string of the command name. */ const char *ttcmdidtostr(int id); /* tricks for backward compatibility */ #define tcrdbqrysetmax(TC_tdb, TC_max) \ tcrdbqrysetlimit((TC_tdb), (TC_max), 0) __TTUTIL_CLINKAGEEND #endif /* duplication check */ /* END OF FILE */