redis-server 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (290) hide show
  1. data/LICENSE +38 -0
  2. data/README.md +33 -0
  3. data/bin/redis +114 -0
  4. data/redis/Makefile +5 -0
  5. data/redis/extconf.rb +3 -0
  6. data/redis/redis-2.2.11/00-RELEASENOTES +199 -0
  7. data/redis/redis-2.2.11/BUGS +1 -0
  8. data/redis/redis-2.2.11/CONTRIBUTING +13 -0
  9. data/redis/redis-2.2.11/COPYING +10 -0
  10. data/redis/redis-2.2.11/Changelog +1032 -0
  11. data/redis/redis-2.2.11/INSTALL +30 -0
  12. data/redis/redis-2.2.11/Makefile +22 -0
  13. data/redis/redis-2.2.11/README +83 -0
  14. data/redis/redis-2.2.11/TODO +4 -0
  15. data/redis/redis-2.2.11/client-libraries/README +11 -0
  16. data/redis/redis-2.2.11/deps/hiredis/COPYING +10 -0
  17. data/redis/redis-2.2.11/deps/hiredis/Makefile +115 -0
  18. data/redis/redis-2.2.11/deps/hiredis/README.md +311 -0
  19. data/redis/redis-2.2.11/deps/hiredis/TODO +2 -0
  20. data/redis/redis-2.2.11/deps/hiredis/adapters/ae.h +95 -0
  21. data/redis/redis-2.2.11/deps/hiredis/adapters/libev.h +113 -0
  22. data/redis/redis-2.2.11/deps/hiredis/adapters/libevent.h +76 -0
  23. data/redis/redis-2.2.11/deps/hiredis/async.c +321 -0
  24. data/redis/redis-2.2.11/deps/hiredis/async.h +112 -0
  25. data/redis/redis-2.2.11/deps/hiredis/example-ae.c +53 -0
  26. data/redis/redis-2.2.11/deps/hiredis/example-libev.c +47 -0
  27. data/redis/redis-2.2.11/deps/hiredis/example-libevent.c +48 -0
  28. data/redis/redis-2.2.11/deps/hiredis/example.c +67 -0
  29. data/redis/redis-2.2.11/deps/hiredis/fmacros.h +15 -0
  30. data/redis/redis-2.2.11/deps/hiredis/hiredis.c +1058 -0
  31. data/redis/redis-2.2.11/deps/hiredis/hiredis.h +170 -0
  32. data/redis/redis-2.2.11/deps/hiredis/net.c +170 -0
  33. data/redis/redis-2.2.11/deps/hiredis/net.h +43 -0
  34. data/redis/redis-2.2.11/deps/hiredis/sds.c +479 -0
  35. data/redis/redis-2.2.11/deps/hiredis/sds.h +77 -0
  36. data/redis/redis-2.2.11/deps/hiredis/test.c +479 -0
  37. data/redis/redis-2.2.11/deps/hiredis/util.h +40 -0
  38. data/redis/redis-2.2.11/deps/linenoise/Makefile +10 -0
  39. data/redis/redis-2.2.11/deps/linenoise/README.markdown +45 -0
  40. data/redis/redis-2.2.11/deps/linenoise/example.c +27 -0
  41. data/redis/redis-2.2.11/deps/linenoise/linenoise.c +609 -0
  42. data/redis/redis-2.2.11/deps/linenoise/linenoise.h +55 -0
  43. data/redis/redis-2.2.11/design-documents/REDIS-CLUSTER +214 -0
  44. data/redis/redis-2.2.11/design-documents/REDIS-CLUSTER-2 +343 -0
  45. data/redis/redis-2.2.11/doc/AppendCommand.html +48 -0
  46. data/redis/redis-2.2.11/doc/AppendOnlyFileHowto.html +41 -0
  47. data/redis/redis-2.2.11/doc/AuthCommand.html +39 -0
  48. data/redis/redis-2.2.11/doc/Benchmarks.html +129 -0
  49. data/redis/redis-2.2.11/doc/BgrewriteaofCommand.html +41 -0
  50. data/redis/redis-2.2.11/doc/BgsaveCommand.html +39 -0
  51. data/redis/redis-2.2.11/doc/BlpopCommand.html +51 -0
  52. data/redis/redis-2.2.11/doc/BrpoplpushCommand.html +39 -0
  53. data/redis/redis-2.2.11/doc/CommandReference.html +47 -0
  54. data/redis/redis-2.2.11/doc/Comparisons.html +42 -0
  55. data/redis/redis-2.2.11/doc/ConfigCommand.html +76 -0
  56. data/redis/redis-2.2.11/doc/Configuration.html +38 -0
  57. data/redis/redis-2.2.11/doc/ConnectionHandlingSidebar.html +36 -0
  58. data/redis/redis-2.2.11/doc/ControlCommandsSidebar.html +36 -0
  59. data/redis/redis-2.2.11/doc/Credits.html +38 -0
  60. data/redis/redis-2.2.11/doc/DbsizeCommand.html +38 -0
  61. data/redis/redis-2.2.11/doc/DelCommand.html +41 -0
  62. data/redis/redis-2.2.11/doc/DesignPatterns.html +37 -0
  63. data/redis/redis-2.2.11/doc/EventLibray.html +44 -0
  64. data/redis/redis-2.2.11/doc/ExistsCommand.html +42 -0
  65. data/redis/redis-2.2.11/doc/ExpireCommand.html +96 -0
  66. data/redis/redis-2.2.11/doc/FAQ.html +70 -0
  67. data/redis/redis-2.2.11/doc/Features.html +38 -0
  68. data/redis/redis-2.2.11/doc/FlushallCommand.html +39 -0
  69. data/redis/redis-2.2.11/doc/FlushdbCommand.html +39 -0
  70. data/redis/redis-2.2.11/doc/FromSqlToDataStructures.html +37 -0
  71. data/redis/redis-2.2.11/doc/GenericCommandsSidebar.html +36 -0
  72. data/redis/redis-2.2.11/doc/GetCommand.html +39 -0
  73. data/redis/redis-2.2.11/doc/GetbitCommand.html +39 -0
  74. data/redis/redis-2.2.11/doc/GetsetCommand.html +38 -0
  75. data/redis/redis-2.2.11/doc/HackingStrings.html +83 -0
  76. data/redis/redis-2.2.11/doc/HashCommandsSidebar.html +36 -0
  77. data/redis/redis-2.2.11/doc/Hashes.html +37 -0
  78. data/redis/redis-2.2.11/doc/HdelCommand.html +39 -0
  79. data/redis/redis-2.2.11/doc/HexistsCommand.html +39 -0
  80. data/redis/redis-2.2.11/doc/HgetCommand.html +39 -0
  81. data/redis/redis-2.2.11/doc/HgetallCommand.html +40 -0
  82. data/redis/redis-2.2.11/doc/HincrbyCommand.html +45 -0
  83. data/redis/redis-2.2.11/doc/HlenCommand.html +38 -0
  84. data/redis/redis-2.2.11/doc/HmgetCommand.html +40 -0
  85. data/redis/redis-2.2.11/doc/HmsetCommand.html +40 -0
  86. data/redis/redis-2.2.11/doc/HsetCommand.html +40 -0
  87. data/redis/redis-2.2.11/doc/HsetnxCommand.html +41 -0
  88. data/redis/redis-2.2.11/doc/IncrCommand.html +43 -0
  89. data/redis/redis-2.2.11/doc/InfoCommand.html +48 -0
  90. data/redis/redis-2.2.11/doc/IntroductionToRedisDataTypes.html +152 -0
  91. data/redis/redis-2.2.11/doc/KeysCommand.html +43 -0
  92. data/redis/redis-2.2.11/doc/LastsaveCommand.html +39 -0
  93. data/redis/redis-2.2.11/doc/LindexCommand.html +40 -0
  94. data/redis/redis-2.2.11/doc/ListCommandsSidebar.html +36 -0
  95. data/redis/redis-2.2.11/doc/Lists.html +42 -0
  96. data/redis/redis-2.2.11/doc/LlenCommand.html +41 -0
  97. data/redis/redis-2.2.11/doc/LpopCommand.html +41 -0
  98. data/redis/redis-2.2.11/doc/LrangeCommand.html +47 -0
  99. data/redis/redis-2.2.11/doc/LremCommand.html +41 -0
  100. data/redis/redis-2.2.11/doc/LsetCommand.html +38 -0
  101. data/redis/redis-2.2.11/doc/LtrimCommand.html +47 -0
  102. data/redis/redis-2.2.11/doc/MgetCommand.html +52 -0
  103. data/redis/redis-2.2.11/doc/MonitorCommand.html +63 -0
  104. data/redis/redis-2.2.11/doc/MoveCommand.html +42 -0
  105. data/redis/redis-2.2.11/doc/MsetCommand.html +44 -0
  106. data/redis/redis-2.2.11/doc/MultiExecCommand.html +166 -0
  107. data/redis/redis-2.2.11/doc/NonexistentCommands.html +51 -0
  108. data/redis/redis-2.2.11/doc/ObjectHashMappers.html +39 -0
  109. data/redis/redis-2.2.11/doc/Pipelining.html +36 -0
  110. data/redis/redis-2.2.11/doc/ProgrammingExamples.html +38 -0
  111. data/redis/redis-2.2.11/doc/ProtocolSpecification.html +137 -0
  112. data/redis/redis-2.2.11/doc/PublishSubscribe.html +115 -0
  113. data/redis/redis-2.2.11/doc/QuickStart.html +68 -0
  114. data/redis/redis-2.2.11/doc/QuitCommand.html +38 -0
  115. data/redis/redis-2.2.11/doc/README.html +119 -0
  116. data/redis/redis-2.2.11/doc/RandomkeyCommand.html +39 -0
  117. data/redis/redis-2.2.11/doc/Redis0100ChangeLog.html +67 -0
  118. data/redis/redis-2.2.11/doc/Redis0900ChangeLog.html +56 -0
  119. data/redis/redis-2.2.11/doc/RedisBigData.html +61 -0
  120. data/redis/redis-2.2.11/doc/RedisCLI.html +37 -0
  121. data/redis/redis-2.2.11/doc/RedisEventLibrary.html +70 -0
  122. data/redis/redis-2.2.11/doc/RedisGuides.html +37 -0
  123. data/redis/redis-2.2.11/doc/RedisInternals.html +38 -0
  124. data/redis/redis-2.2.11/doc/RedisPipelining.html +93 -0
  125. data/redis/redis-2.2.11/doc/RedisStatus.html +56 -0
  126. data/redis/redis-2.2.11/doc/Redis_1_2_0_Changelog.html +40 -0
  127. data/redis/redis-2.2.11/doc/Redis_2_0_0_Changelog.html +62 -0
  128. data/redis/redis-2.2.11/doc/Redis_2_0_Whats_new.html +59 -0
  129. data/redis/redis-2.2.11/doc/RenameCommand.html +39 -0
  130. data/redis/redis-2.2.11/doc/RenamenxCommand.html +42 -0
  131. data/redis/redis-2.2.11/doc/ReplicationHowto.html +43 -0
  132. data/redis/redis-2.2.11/doc/ReplyTypes.html +42 -0
  133. data/redis/redis-2.2.11/doc/RoadMap.html +38 -0
  134. data/redis/redis-2.2.11/doc/RpoplpushCommand.html +44 -0
  135. data/redis/redis-2.2.11/doc/RpushCommand.html +40 -0
  136. data/redis/redis-2.2.11/doc/SaddCommand.html +41 -0
  137. data/redis/redis-2.2.11/doc/SaveCommand.html +39 -0
  138. data/redis/redis-2.2.11/doc/ScardCommand.html +41 -0
  139. data/redis/redis-2.2.11/doc/SdiffCommand.html +45 -0
  140. data/redis/redis-2.2.11/doc/SdiffstoreCommand.html +38 -0
  141. data/redis/redis-2.2.11/doc/SelectCommand.html +39 -0
  142. data/redis/redis-2.2.11/doc/SetCommand.html +39 -0
  143. data/redis/redis-2.2.11/doc/SetCommandsSidebar.html +36 -0
  144. data/redis/redis-2.2.11/doc/SetbitCommand.html +45 -0
  145. data/redis/redis-2.2.11/doc/SetexCommand.html +42 -0
  146. data/redis/redis-2.2.11/doc/SetnxCommand.html +51 -0
  147. data/redis/redis-2.2.11/doc/SetrangeCommand.html +58 -0
  148. data/redis/redis-2.2.11/doc/Sets.html +36 -0
  149. data/redis/redis-2.2.11/doc/ShutdownCommand.html +39 -0
  150. data/redis/redis-2.2.11/doc/SideBar.html +36 -0
  151. data/redis/redis-2.2.11/doc/SinterCommand.html +40 -0
  152. data/redis/redis-2.2.11/doc/SinterstoreCommand.html +39 -0
  153. data/redis/redis-2.2.11/doc/SismemberCommand.html +42 -0
  154. data/redis/redis-2.2.11/doc/SlaveofCommand.html +41 -0
  155. data/redis/redis-2.2.11/doc/SmembersCommand.html +38 -0
  156. data/redis/redis-2.2.11/doc/SmoveCommand.html +44 -0
  157. data/redis/redis-2.2.11/doc/SortCommand.html +75 -0
  158. data/redis/redis-2.2.11/doc/SortedSetCommandsSidebar.html +36 -0
  159. data/redis/redis-2.2.11/doc/SortedSets.html +36 -0
  160. data/redis/redis-2.2.11/doc/Speed.html +38 -0
  161. data/redis/redis-2.2.11/doc/SponsorshipHistory.html +38 -0
  162. data/redis/redis-2.2.11/doc/SpopCommand.html +40 -0
  163. data/redis/redis-2.2.11/doc/SrandmemberCommand.html +40 -0
  164. data/redis/redis-2.2.11/doc/SremCommand.html +42 -0
  165. data/redis/redis-2.2.11/doc/StringCommandsSidebar.html +36 -0
  166. data/redis/redis-2.2.11/doc/Strings.html +37 -0
  167. data/redis/redis-2.2.11/doc/StrlenCommand.html +39 -0
  168. data/redis/redis-2.2.11/doc/SubstrCommand.html +52 -0
  169. data/redis/redis-2.2.11/doc/SunionCommand.html +40 -0
  170. data/redis/redis-2.2.11/doc/SunionstoreCommand.html +38 -0
  171. data/redis/redis-2.2.11/doc/SupportedLanguages.html +60 -0
  172. data/redis/redis-2.2.11/doc/SupportedPlatforms.html +37 -0
  173. data/redis/redis-2.2.11/doc/TemplateCommand.html +38 -0
  174. data/redis/redis-2.2.11/doc/TtlCommand.html +38 -0
  175. data/redis/redis-2.2.11/doc/TwitterAlikeExample.html +250 -0
  176. data/redis/redis-2.2.11/doc/TypeCommand.html +46 -0
  177. data/redis/redis-2.2.11/doc/UnstableSource.html +39 -0
  178. data/redis/redis-2.2.11/doc/VirtualMemorySpecification.html +156 -0
  179. data/redis/redis-2.2.11/doc/VirtualMemoryUserGuide.html +66 -0
  180. data/redis/redis-2.2.11/doc/ZaddCommand.html +43 -0
  181. data/redis/redis-2.2.11/doc/ZcardCommand.html +41 -0
  182. data/redis/redis-2.2.11/doc/ZincrbyCommand.html +42 -0
  183. data/redis/redis-2.2.11/doc/ZrangeCommand.html +42 -0
  184. data/redis/redis-2.2.11/doc/ZrangebyscoreCommand.html +77 -0
  185. data/redis/redis-2.2.11/doc/ZrankCommand.html +43 -0
  186. data/redis/redis-2.2.11/doc/ZremCommand.html +42 -0
  187. data/redis/redis-2.2.11/doc/ZremrangebyrankCommand.html +39 -0
  188. data/redis/redis-2.2.11/doc/ZremrangebyscoreCommand.html +39 -0
  189. data/redis/redis-2.2.11/doc/ZscoreCommand.html +41 -0
  190. data/redis/redis-2.2.11/doc/ZunionCommand.html +42 -0
  191. data/redis/redis-2.2.11/doc/ZunionstoreCommand.html +43 -0
  192. data/redis/redis-2.2.11/doc/index.html +43 -0
  193. data/redis/redis-2.2.11/doc/redis.png +0 -0
  194. data/redis/redis-2.2.11/doc/style.css +25 -0
  195. data/redis/redis-2.2.11/redis.conf +417 -0
  196. data/redis/redis-2.2.11/src/Makefile +177 -0
  197. data/redis/redis-2.2.11/src/adlist.c +325 -0
  198. data/redis/redis-2.2.11/src/adlist.h +92 -0
  199. data/redis/redis-2.2.11/src/ae.c +390 -0
  200. data/redis/redis-2.2.11/src/ae.h +117 -0
  201. data/redis/redis-2.2.11/src/ae_epoll.c +91 -0
  202. data/redis/redis-2.2.11/src/ae_kqueue.c +93 -0
  203. data/redis/redis-2.2.11/src/ae_select.c +72 -0
  204. data/redis/redis-2.2.11/src/anet.c +347 -0
  205. data/redis/redis-2.2.11/src/anet.h +57 -0
  206. data/redis/redis-2.2.11/src/aof.c +675 -0
  207. data/redis/redis-2.2.11/src/config.c +627 -0
  208. data/redis/redis-2.2.11/src/config.h +64 -0
  209. data/redis/redis-2.2.11/src/db.c +543 -0
  210. data/redis/redis-2.2.11/src/debug.c +314 -0
  211. data/redis/redis-2.2.11/src/dict.c +721 -0
  212. data/redis/redis-2.2.11/src/dict.h +156 -0
  213. data/redis/redis-2.2.11/src/fmacros.h +15 -0
  214. data/redis/redis-2.2.11/src/help.h +638 -0
  215. data/redis/redis-2.2.11/src/intset.c +422 -0
  216. data/redis/redis-2.2.11/src/intset.h +19 -0
  217. data/redis/redis-2.2.11/src/lzf.h +100 -0
  218. data/redis/redis-2.2.11/src/lzfP.h +159 -0
  219. data/redis/redis-2.2.11/src/lzf_c.c +295 -0
  220. data/redis/redis-2.2.11/src/lzf_d.c +150 -0
  221. data/redis/redis-2.2.11/src/mkreleasehdr.sh +9 -0
  222. data/redis/redis-2.2.11/src/multi.c +268 -0
  223. data/redis/redis-2.2.11/src/networking.c +899 -0
  224. data/redis/redis-2.2.11/src/object.c +484 -0
  225. data/redis/redis-2.2.11/src/pqsort.c +197 -0
  226. data/redis/redis-2.2.11/src/pqsort.h +15 -0
  227. data/redis/redis-2.2.11/src/pubsub.c +267 -0
  228. data/redis/redis-2.2.11/src/rdb.c +1020 -0
  229. data/redis/redis-2.2.11/src/redis-benchmark.c +530 -0
  230. data/redis/redis-2.2.11/src/redis-check-aof.c +185 -0
  231. data/redis/redis-2.2.11/src/redis-check-dump.c +681 -0
  232. data/redis/redis-2.2.11/src/redis-cli.c +773 -0
  233. data/redis/redis-2.2.11/src/redis.c +1677 -0
  234. data/redis/redis-2.2.11/src/redis.h +1022 -0
  235. data/redis/redis-2.2.11/src/release.c +13 -0
  236. data/redis/redis-2.2.11/src/replication.c +557 -0
  237. data/redis/redis-2.2.11/src/sds.c +639 -0
  238. data/redis/redis-2.2.11/src/sds.h +78 -0
  239. data/redis/redis-2.2.11/src/sha1.c +276 -0
  240. data/redis/redis-2.2.11/src/sha1.h +17 -0
  241. data/redis/redis-2.2.11/src/solarisfixes.h +22 -0
  242. data/redis/redis-2.2.11/src/sort.c +389 -0
  243. data/redis/redis-2.2.11/src/syncio.c +154 -0
  244. data/redis/redis-2.2.11/src/t_hash.c +476 -0
  245. data/redis/redis-2.2.11/src/t_list.c +986 -0
  246. data/redis/redis-2.2.11/src/t_set.c +610 -0
  247. data/redis/redis-2.2.11/src/t_string.c +438 -0
  248. data/redis/redis-2.2.11/src/t_zset.c +1084 -0
  249. data/redis/redis-2.2.11/src/testhelp.h +54 -0
  250. data/redis/redis-2.2.11/src/util.c +243 -0
  251. data/redis/redis-2.2.11/src/valgrind.sup +5 -0
  252. data/redis/redis-2.2.11/src/version.h +1 -0
  253. data/redis/redis-2.2.11/src/vm.c +1149 -0
  254. data/redis/redis-2.2.11/src/ziplist.c +1323 -0
  255. data/redis/redis-2.2.11/src/ziplist.h +15 -0
  256. data/redis/redis-2.2.11/src/zipmap.c +455 -0
  257. data/redis/redis-2.2.11/src/zipmap.h +48 -0
  258. data/redis/redis-2.2.11/src/zmalloc.c +278 -0
  259. data/redis/redis-2.2.11/src/zmalloc.h +47 -0
  260. data/redis/redis-2.2.11/tests/assets/default.conf +308 -0
  261. data/redis/redis-2.2.11/tests/integration/aof.tcl +104 -0
  262. data/redis/redis-2.2.11/tests/integration/redis-cli.tcl +208 -0
  263. data/redis/redis-2.2.11/tests/integration/replication.tcl +98 -0
  264. data/redis/redis-2.2.11/tests/support/redis.tcl +241 -0
  265. data/redis/redis-2.2.11/tests/support/server.tcl +294 -0
  266. data/redis/redis-2.2.11/tests/support/test.tcl +190 -0
  267. data/redis/redis-2.2.11/tests/support/tmpfile.tcl +15 -0
  268. data/redis/redis-2.2.11/tests/support/util.tcl +296 -0
  269. data/redis/redis-2.2.11/tests/test_helper.tcl +221 -0
  270. data/redis/redis-2.2.11/tests/unit/auth.tcl +15 -0
  271. data/redis/redis-2.2.11/tests/unit/basic.tcl +616 -0
  272. data/redis/redis-2.2.11/tests/unit/cas.tcl +135 -0
  273. data/redis/redis-2.2.11/tests/unit/expire.tcl +74 -0
  274. data/redis/redis-2.2.11/tests/unit/other.tcl +240 -0
  275. data/redis/redis-2.2.11/tests/unit/printver.tcl +6 -0
  276. data/redis/redis-2.2.11/tests/unit/protocol.tcl +62 -0
  277. data/redis/redis-2.2.11/tests/unit/pubsub.tcl +195 -0
  278. data/redis/redis-2.2.11/tests/unit/quit.tcl +40 -0
  279. data/redis/redis-2.2.11/tests/unit/sort.tcl +189 -0
  280. data/redis/redis-2.2.11/tests/unit/type/hash.tcl +300 -0
  281. data/redis/redis-2.2.11/tests/unit/type/list.tcl +819 -0
  282. data/redis/redis-2.2.11/tests/unit/type/set.tcl +334 -0
  283. data/redis/redis-2.2.11/tests/unit/type/zset.tcl +587 -0
  284. data/redis/redis-2.2.11/utils/build-static-symbols.tcl +22 -0
  285. data/redis/redis-2.2.11/utils/generate-command-help.rb +112 -0
  286. data/redis/redis-2.2.11/utils/mktarball.sh +13 -0
  287. data/redis/redis-2.2.11/utils/redis-copy.rb +78 -0
  288. data/redis/redis-2.2.11/utils/redis-sha1.rb +52 -0
  289. data/redis/redis-2.2.11/utils/redis_init_script +42 -0
  290. metadata +362 -0
@@ -0,0 +1,150 @@
1
+ /*
2
+ * Copyright (c) 2000-2007 Marc Alexander Lehmann <schmorp@schmorp.de>
3
+ *
4
+ * Redistribution and use in source and binary forms, with or without modifica-
5
+ * tion, are permitted provided that the following conditions are met:
6
+ *
7
+ * 1. Redistributions of source code must retain the above copyright notice,
8
+ * this list of conditions and the following disclaimer.
9
+ *
10
+ * 2. Redistributions in binary form must reproduce the above copyright
11
+ * notice, this list of conditions and the following disclaimer in the
12
+ * documentation and/or other materials provided with the distribution.
13
+ *
14
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
15
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER-
16
+ * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
17
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE-
18
+ * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
20
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
21
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH-
22
+ * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
23
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
24
+ *
25
+ * Alternatively, the contents of this file may be used under the terms of
26
+ * the GNU General Public License ("GPL") version 2 or any later version,
27
+ * in which case the provisions of the GPL are applicable instead of
28
+ * the above. If you wish to allow the use of your version of this file
29
+ * only under the terms of the GPL and not to allow others to use your
30
+ * version of this file under the BSD license, indicate your decision
31
+ * by deleting the provisions above and replace them with the notice
32
+ * and other provisions required by the GPL. If you do not delete the
33
+ * provisions above, a recipient may use your version of this file under
34
+ * either the BSD or the GPL.
35
+ */
36
+
37
+ #include "lzfP.h"
38
+
39
+ #if AVOID_ERRNO
40
+ # define SET_ERRNO(n)
41
+ #else
42
+ # include <errno.h>
43
+ # define SET_ERRNO(n) errno = (n)
44
+ #endif
45
+
46
+ /*
47
+ #if (__i386 || __amd64) && __GNUC__ >= 3
48
+ # define lzf_movsb(dst, src, len) \
49
+ asm ("rep movsb" \
50
+ : "=D" (dst), "=S" (src), "=c" (len) \
51
+ : "0" (dst), "1" (src), "2" (len));
52
+ #endif
53
+ */
54
+
55
+ unsigned int
56
+ lzf_decompress (const void *const in_data, unsigned int in_len,
57
+ void *out_data, unsigned int out_len)
58
+ {
59
+ u8 const *ip = (const u8 *)in_data;
60
+ u8 *op = (u8 *)out_data;
61
+ u8 const *const in_end = ip + in_len;
62
+ u8 *const out_end = op + out_len;
63
+
64
+ do
65
+ {
66
+ unsigned int ctrl = *ip++;
67
+
68
+ if (ctrl < (1 << 5)) /* literal run */
69
+ {
70
+ ctrl++;
71
+
72
+ if (op + ctrl > out_end)
73
+ {
74
+ SET_ERRNO (E2BIG);
75
+ return 0;
76
+ }
77
+
78
+ #if CHECK_INPUT
79
+ if (ip + ctrl > in_end)
80
+ {
81
+ SET_ERRNO (EINVAL);
82
+ return 0;
83
+ }
84
+ #endif
85
+
86
+ #ifdef lzf_movsb
87
+ lzf_movsb (op, ip, ctrl);
88
+ #else
89
+ do
90
+ *op++ = *ip++;
91
+ while (--ctrl);
92
+ #endif
93
+ }
94
+ else /* back reference */
95
+ {
96
+ unsigned int len = ctrl >> 5;
97
+
98
+ u8 *ref = op - ((ctrl & 0x1f) << 8) - 1;
99
+
100
+ #if CHECK_INPUT
101
+ if (ip >= in_end)
102
+ {
103
+ SET_ERRNO (EINVAL);
104
+ return 0;
105
+ }
106
+ #endif
107
+ if (len == 7)
108
+ {
109
+ len += *ip++;
110
+ #if CHECK_INPUT
111
+ if (ip >= in_end)
112
+ {
113
+ SET_ERRNO (EINVAL);
114
+ return 0;
115
+ }
116
+ #endif
117
+ }
118
+
119
+ ref -= *ip++;
120
+
121
+ if (op + len + 2 > out_end)
122
+ {
123
+ SET_ERRNO (E2BIG);
124
+ return 0;
125
+ }
126
+
127
+ if (ref < (u8 *)out_data)
128
+ {
129
+ SET_ERRNO (EINVAL);
130
+ return 0;
131
+ }
132
+
133
+ #ifdef lzf_movsb
134
+ len += 2;
135
+ lzf_movsb (op, ref, len);
136
+ #else
137
+ *op++ = *ref++;
138
+ *op++ = *ref++;
139
+
140
+ do
141
+ *op++ = *ref++;
142
+ while (--len);
143
+ #endif
144
+ }
145
+ }
146
+ while (ip < in_end);
147
+
148
+ return op - (u8 *)out_data;
149
+ }
150
+
@@ -0,0 +1,9 @@
1
+ #!/bin/sh
2
+ GIT_SHA1=`(git show-ref --head --hash=8 2> /dev/null || echo 00000000) | head -n1`
3
+ GIT_DIRTY=`git diff 2> /dev/null | wc -l`
4
+ test -f release.h || touch release.h
5
+ (cat release.h | grep SHA1 | grep $GIT_SHA1) && \
6
+ (cat release.h | grep DIRTY | grep $GIT_DIRTY) && exit 0 # Already uptodate
7
+ echo "#define REDIS_GIT_SHA1 \"$GIT_SHA1\"" > release.h
8
+ echo "#define REDIS_GIT_DIRTY \"$GIT_DIRTY\"" >> release.h
9
+ touch release.c # Force recompile of release.c
@@ -0,0 +1,268 @@
1
+ #include "redis.h"
2
+
3
+ /* ================================ MULTI/EXEC ============================== */
4
+
5
+ /* Client state initialization for MULTI/EXEC */
6
+ void initClientMultiState(redisClient *c) {
7
+ c->mstate.commands = NULL;
8
+ c->mstate.count = 0;
9
+ }
10
+
11
+ /* Release all the resources associated with MULTI/EXEC state */
12
+ void freeClientMultiState(redisClient *c) {
13
+ int j;
14
+
15
+ for (j = 0; j < c->mstate.count; j++) {
16
+ int i;
17
+ multiCmd *mc = c->mstate.commands+j;
18
+
19
+ for (i = 0; i < mc->argc; i++)
20
+ decrRefCount(mc->argv[i]);
21
+ zfree(mc->argv);
22
+ }
23
+ zfree(c->mstate.commands);
24
+ }
25
+
26
+ /* Add a new command into the MULTI commands queue */
27
+ void queueMultiCommand(redisClient *c, struct redisCommand *cmd) {
28
+ multiCmd *mc;
29
+ int j;
30
+
31
+ c->mstate.commands = zrealloc(c->mstate.commands,
32
+ sizeof(multiCmd)*(c->mstate.count+1));
33
+ mc = c->mstate.commands+c->mstate.count;
34
+ mc->cmd = cmd;
35
+ mc->argc = c->argc;
36
+ mc->argv = zmalloc(sizeof(robj*)*c->argc);
37
+ memcpy(mc->argv,c->argv,sizeof(robj*)*c->argc);
38
+ for (j = 0; j < c->argc; j++)
39
+ incrRefCount(mc->argv[j]);
40
+ c->mstate.count++;
41
+ }
42
+
43
+ void multiCommand(redisClient *c) {
44
+ if (c->flags & REDIS_MULTI) {
45
+ addReplyError(c,"MULTI calls can not be nested");
46
+ return;
47
+ }
48
+ c->flags |= REDIS_MULTI;
49
+ addReply(c,shared.ok);
50
+ }
51
+
52
+ void discardCommand(redisClient *c) {
53
+ if (!(c->flags & REDIS_MULTI)) {
54
+ addReplyError(c,"DISCARD without MULTI");
55
+ return;
56
+ }
57
+
58
+ freeClientMultiState(c);
59
+ initClientMultiState(c);
60
+ c->flags &= (~REDIS_MULTI);
61
+ unwatchAllKeys(c);
62
+ addReply(c,shared.ok);
63
+ }
64
+
65
+ /* Send a MULTI command to all the slaves and AOF file. Check the execCommand
66
+ * implememntation for more information. */
67
+ void execCommandReplicateMulti(redisClient *c) {
68
+ robj *multistring = createStringObject("MULTI",5);
69
+
70
+ if (server.appendonly)
71
+ feedAppendOnlyFile(server.multiCommand,c->db->id,&multistring,1);
72
+ if (listLength(server.slaves))
73
+ replicationFeedSlaves(server.slaves,c->db->id,&multistring,1);
74
+ decrRefCount(multistring);
75
+ }
76
+
77
+ void execCommand(redisClient *c) {
78
+ int j;
79
+ robj **orig_argv;
80
+ int orig_argc;
81
+
82
+ if (!(c->flags & REDIS_MULTI)) {
83
+ addReplyError(c,"EXEC without MULTI");
84
+ return;
85
+ }
86
+
87
+ /* Check if we need to abort the EXEC if some WATCHed key was touched.
88
+ * A failed EXEC will return a multi bulk nil object. */
89
+ if (c->flags & REDIS_DIRTY_CAS) {
90
+ freeClientMultiState(c);
91
+ initClientMultiState(c);
92
+ c->flags &= ~(REDIS_MULTI|REDIS_DIRTY_CAS);
93
+ unwatchAllKeys(c);
94
+ addReply(c,shared.nullmultibulk);
95
+ return;
96
+ }
97
+
98
+ /* Replicate a MULTI request now that we are sure the block is executed.
99
+ * This way we'll deliver the MULTI/..../EXEC block as a whole and
100
+ * both the AOF and the replication link will have the same consistency
101
+ * and atomicity guarantees. */
102
+ execCommandReplicateMulti(c);
103
+
104
+ /* Exec all the queued commands */
105
+ unwatchAllKeys(c); /* Unwatch ASAP otherwise we'll waste CPU cycles */
106
+ orig_argv = c->argv;
107
+ orig_argc = c->argc;
108
+ addReplyMultiBulkLen(c,c->mstate.count);
109
+ for (j = 0; j < c->mstate.count; j++) {
110
+ c->argc = c->mstate.commands[j].argc;
111
+ c->argv = c->mstate.commands[j].argv;
112
+ call(c,c->mstate.commands[j].cmd);
113
+
114
+ /* Commands may alter argc/argv, restore mstate. */
115
+ c->mstate.commands[j].argc = c->argc;
116
+ c->mstate.commands[j].argv = c->argv;
117
+ }
118
+ c->argv = orig_argv;
119
+ c->argc = orig_argc;
120
+ freeClientMultiState(c);
121
+ initClientMultiState(c);
122
+ c->flags &= ~(REDIS_MULTI|REDIS_DIRTY_CAS);
123
+ /* Make sure the EXEC command is always replicated / AOF, since we
124
+ * always send the MULTI command (we can't know beforehand if the
125
+ * next operations will contain at least a modification to the DB). */
126
+ server.dirty++;
127
+ }
128
+
129
+ /* ===================== WATCH (CAS alike for MULTI/EXEC) ===================
130
+ *
131
+ * The implementation uses a per-DB hash table mapping keys to list of clients
132
+ * WATCHing those keys, so that given a key that is going to be modified
133
+ * we can mark all the associated clients as dirty.
134
+ *
135
+ * Also every client contains a list of WATCHed keys so that's possible to
136
+ * un-watch such keys when the client is freed or when UNWATCH is called. */
137
+
138
+ /* In the client->watched_keys list we need to use watchedKey structures
139
+ * as in order to identify a key in Redis we need both the key name and the
140
+ * DB */
141
+ typedef struct watchedKey {
142
+ robj *key;
143
+ redisDb *db;
144
+ } watchedKey;
145
+
146
+ /* Watch for the specified key */
147
+ void watchForKey(redisClient *c, robj *key) {
148
+ list *clients = NULL;
149
+ listIter li;
150
+ listNode *ln;
151
+ watchedKey *wk;
152
+
153
+ /* Check if we are already watching for this key */
154
+ listRewind(c->watched_keys,&li);
155
+ while((ln = listNext(&li))) {
156
+ wk = listNodeValue(ln);
157
+ if (wk->db == c->db && equalStringObjects(key,wk->key))
158
+ return; /* Key already watched */
159
+ }
160
+ /* This key is not already watched in this DB. Let's add it */
161
+ clients = dictFetchValue(c->db->watched_keys,key);
162
+ if (!clients) {
163
+ clients = listCreate();
164
+ dictAdd(c->db->watched_keys,key,clients);
165
+ incrRefCount(key);
166
+ }
167
+ listAddNodeTail(clients,c);
168
+ /* Add the new key to the lits of keys watched by this client */
169
+ wk = zmalloc(sizeof(*wk));
170
+ wk->key = key;
171
+ wk->db = c->db;
172
+ incrRefCount(key);
173
+ listAddNodeTail(c->watched_keys,wk);
174
+ }
175
+
176
+ /* Unwatch all the keys watched by this client. To clean the EXEC dirty
177
+ * flag is up to the caller. */
178
+ void unwatchAllKeys(redisClient *c) {
179
+ listIter li;
180
+ listNode *ln;
181
+
182
+ if (listLength(c->watched_keys) == 0) return;
183
+ listRewind(c->watched_keys,&li);
184
+ while((ln = listNext(&li))) {
185
+ list *clients;
186
+ watchedKey *wk;
187
+
188
+ /* Lookup the watched key -> clients list and remove the client
189
+ * from the list */
190
+ wk = listNodeValue(ln);
191
+ clients = dictFetchValue(wk->db->watched_keys, wk->key);
192
+ redisAssert(clients != NULL);
193
+ listDelNode(clients,listSearchKey(clients,c));
194
+ /* Kill the entry at all if this was the only client */
195
+ if (listLength(clients) == 0)
196
+ dictDelete(wk->db->watched_keys, wk->key);
197
+ /* Remove this watched key from the client->watched list */
198
+ listDelNode(c->watched_keys,ln);
199
+ decrRefCount(wk->key);
200
+ zfree(wk);
201
+ }
202
+ }
203
+
204
+ /* "Touch" a key, so that if this key is being WATCHed by some client the
205
+ * next EXEC will fail. */
206
+ void touchWatchedKey(redisDb *db, robj *key) {
207
+ list *clients;
208
+ listIter li;
209
+ listNode *ln;
210
+
211
+ if (dictSize(db->watched_keys) == 0) return;
212
+ clients = dictFetchValue(db->watched_keys, key);
213
+ if (!clients) return;
214
+
215
+ /* Mark all the clients watching this key as REDIS_DIRTY_CAS */
216
+ /* Check if we are already watching for this key */
217
+ listRewind(clients,&li);
218
+ while((ln = listNext(&li))) {
219
+ redisClient *c = listNodeValue(ln);
220
+
221
+ c->flags |= REDIS_DIRTY_CAS;
222
+ }
223
+ }
224
+
225
+ /* On FLUSHDB or FLUSHALL all the watched keys that are present before the
226
+ * flush but will be deleted as effect of the flushing operation should
227
+ * be touched. "dbid" is the DB that's getting the flush. -1 if it is
228
+ * a FLUSHALL operation (all the DBs flushed). */
229
+ void touchWatchedKeysOnFlush(int dbid) {
230
+ listIter li1, li2;
231
+ listNode *ln;
232
+
233
+ /* For every client, check all the waited keys */
234
+ listRewind(server.clients,&li1);
235
+ while((ln = listNext(&li1))) {
236
+ redisClient *c = listNodeValue(ln);
237
+ listRewind(c->watched_keys,&li2);
238
+ while((ln = listNext(&li2))) {
239
+ watchedKey *wk = listNodeValue(ln);
240
+
241
+ /* For every watched key matching the specified DB, if the
242
+ * key exists, mark the client as dirty, as the key will be
243
+ * removed. */
244
+ if (dbid == -1 || wk->db->id == dbid) {
245
+ if (dictFind(wk->db->dict, wk->key->ptr) != NULL)
246
+ c->flags |= REDIS_DIRTY_CAS;
247
+ }
248
+ }
249
+ }
250
+ }
251
+
252
+ void watchCommand(redisClient *c) {
253
+ int j;
254
+
255
+ if (c->flags & REDIS_MULTI) {
256
+ addReplyError(c,"WATCH inside MULTI is not allowed");
257
+ return;
258
+ }
259
+ for (j = 1; j < c->argc; j++)
260
+ watchForKey(c,c->argv[j]);
261
+ addReply(c,shared.ok);
262
+ }
263
+
264
+ void unwatchCommand(redisClient *c) {
265
+ unwatchAllKeys(c);
266
+ c->flags &= (~REDIS_DIRTY_CAS);
267
+ addReply(c,shared.ok);
268
+ }
@@ -0,0 +1,899 @@
1
+ #include "redis.h"
2
+ #include <sys/uio.h>
3
+
4
+ void *dupClientReplyValue(void *o) {
5
+ incrRefCount((robj*)o);
6
+ return o;
7
+ }
8
+
9
+ int listMatchObjects(void *a, void *b) {
10
+ return equalStringObjects(a,b);
11
+ }
12
+
13
+ redisClient *createClient(int fd) {
14
+ redisClient *c = zmalloc(sizeof(redisClient));
15
+ c->bufpos = 0;
16
+
17
+ anetNonBlock(NULL,fd);
18
+ anetTcpNoDelay(NULL,fd);
19
+ if (!c) return NULL;
20
+ if (aeCreateFileEvent(server.el,fd,AE_READABLE,
21
+ readQueryFromClient, c) == AE_ERR)
22
+ {
23
+ close(fd);
24
+ zfree(c);
25
+ return NULL;
26
+ }
27
+
28
+ selectDb(c,0);
29
+ c->fd = fd;
30
+ c->querybuf = sdsempty();
31
+ c->reqtype = 0;
32
+ c->argc = 0;
33
+ c->argv = NULL;
34
+ c->multibulklen = 0;
35
+ c->bulklen = -1;
36
+ c->sentlen = 0;
37
+ c->flags = 0;
38
+ c->lastinteraction = time(NULL);
39
+ c->authenticated = 0;
40
+ c->replstate = REDIS_REPL_NONE;
41
+ c->reply = listCreate();
42
+ listSetFreeMethod(c->reply,decrRefCount);
43
+ listSetDupMethod(c->reply,dupClientReplyValue);
44
+ c->bpop.keys = NULL;
45
+ c->bpop.count = 0;
46
+ c->bpop.timeout = 0;
47
+ c->bpop.target = NULL;
48
+ c->io_keys = listCreate();
49
+ c->watched_keys = listCreate();
50
+ listSetFreeMethod(c->io_keys,decrRefCount);
51
+ c->pubsub_channels = dictCreate(&setDictType,NULL);
52
+ c->pubsub_patterns = listCreate();
53
+ listSetFreeMethod(c->pubsub_patterns,decrRefCount);
54
+ listSetMatchMethod(c->pubsub_patterns,listMatchObjects);
55
+ listAddNodeTail(server.clients,c);
56
+ initClientMultiState(c);
57
+ return c;
58
+ }
59
+
60
+ /* Set the event loop to listen for write events on the client's socket.
61
+ * Typically gets called every time a reply is built. */
62
+ int _installWriteEvent(redisClient *c) {
63
+ if (c->fd <= 0) return REDIS_ERR;
64
+ if (c->bufpos == 0 && listLength(c->reply) == 0 &&
65
+ (c->replstate == REDIS_REPL_NONE ||
66
+ c->replstate == REDIS_REPL_ONLINE) &&
67
+ aeCreateFileEvent(server.el, c->fd, AE_WRITABLE,
68
+ sendReplyToClient, c) == AE_ERR) return REDIS_ERR;
69
+ return REDIS_OK;
70
+ }
71
+
72
+ /* Create a duplicate of the last object in the reply list when
73
+ * it is not exclusively owned by the reply list. */
74
+ robj *dupLastObjectIfNeeded(list *reply) {
75
+ robj *new, *cur;
76
+ listNode *ln;
77
+ redisAssert(listLength(reply) > 0);
78
+ ln = listLast(reply);
79
+ cur = listNodeValue(ln);
80
+ if (cur->refcount > 1) {
81
+ new = dupStringObject(cur);
82
+ decrRefCount(cur);
83
+ listNodeValue(ln) = new;
84
+ }
85
+ return listNodeValue(ln);
86
+ }
87
+
88
+ /* -----------------------------------------------------------------------------
89
+ * Low level functions to add more data to output buffers.
90
+ * -------------------------------------------------------------------------- */
91
+
92
+ int _addReplyToBuffer(redisClient *c, char *s, size_t len) {
93
+ size_t available = sizeof(c->buf)-c->bufpos;
94
+
95
+ if (c->flags & REDIS_CLOSE_AFTER_REPLY) return REDIS_OK;
96
+
97
+ /* If there already are entries in the reply list, we cannot
98
+ * add anything more to the static buffer. */
99
+ if (listLength(c->reply) > 0) return REDIS_ERR;
100
+
101
+ /* Check that the buffer has enough space available for this string. */
102
+ if (len > available) return REDIS_ERR;
103
+
104
+ memcpy(c->buf+c->bufpos,s,len);
105
+ c->bufpos+=len;
106
+ return REDIS_OK;
107
+ }
108
+
109
+ void _addReplyObjectToList(redisClient *c, robj *o) {
110
+ robj *tail;
111
+
112
+ if (c->flags & REDIS_CLOSE_AFTER_REPLY) return;
113
+
114
+ if (listLength(c->reply) == 0) {
115
+ incrRefCount(o);
116
+ listAddNodeTail(c->reply,o);
117
+ } else {
118
+ tail = listNodeValue(listLast(c->reply));
119
+
120
+ /* Append to this object when possible. */
121
+ if (tail->ptr != NULL &&
122
+ sdslen(tail->ptr)+sdslen(o->ptr) <= REDIS_REPLY_CHUNK_BYTES)
123
+ {
124
+ tail = dupLastObjectIfNeeded(c->reply);
125
+ tail->ptr = sdscatlen(tail->ptr,o->ptr,sdslen(o->ptr));
126
+ } else {
127
+ incrRefCount(o);
128
+ listAddNodeTail(c->reply,o);
129
+ }
130
+ }
131
+ }
132
+
133
+ /* This method takes responsibility over the sds. When it is no longer
134
+ * needed it will be free'd, otherwise it ends up in a robj. */
135
+ void _addReplySdsToList(redisClient *c, sds s) {
136
+ robj *tail;
137
+
138
+ if (c->flags & REDIS_CLOSE_AFTER_REPLY) {
139
+ sdsfree(s);
140
+ return;
141
+ }
142
+
143
+ if (listLength(c->reply) == 0) {
144
+ listAddNodeTail(c->reply,createObject(REDIS_STRING,s));
145
+ } else {
146
+ tail = listNodeValue(listLast(c->reply));
147
+
148
+ /* Append to this object when possible. */
149
+ if (tail->ptr != NULL &&
150
+ sdslen(tail->ptr)+sdslen(s) <= REDIS_REPLY_CHUNK_BYTES)
151
+ {
152
+ tail = dupLastObjectIfNeeded(c->reply);
153
+ tail->ptr = sdscatlen(tail->ptr,s,sdslen(s));
154
+ sdsfree(s);
155
+ } else {
156
+ listAddNodeTail(c->reply,createObject(REDIS_STRING,s));
157
+ }
158
+ }
159
+ }
160
+
161
+ void _addReplyStringToList(redisClient *c, char *s, size_t len) {
162
+ robj *tail;
163
+
164
+ if (c->flags & REDIS_CLOSE_AFTER_REPLY) return;
165
+
166
+ if (listLength(c->reply) == 0) {
167
+ listAddNodeTail(c->reply,createStringObject(s,len));
168
+ } else {
169
+ tail = listNodeValue(listLast(c->reply));
170
+
171
+ /* Append to this object when possible. */
172
+ if (tail->ptr != NULL &&
173
+ sdslen(tail->ptr)+len <= REDIS_REPLY_CHUNK_BYTES)
174
+ {
175
+ tail = dupLastObjectIfNeeded(c->reply);
176
+ tail->ptr = sdscatlen(tail->ptr,s,len);
177
+ } else {
178
+ listAddNodeTail(c->reply,createStringObject(s,len));
179
+ }
180
+ }
181
+ }
182
+
183
+ /* -----------------------------------------------------------------------------
184
+ * Higher level functions to queue data on the client output buffer.
185
+ * The following functions are the ones that commands implementations will call.
186
+ * -------------------------------------------------------------------------- */
187
+
188
+ void addReply(redisClient *c, robj *obj) {
189
+ if (_installWriteEvent(c) != REDIS_OK) return;
190
+ redisAssert(!server.vm_enabled || obj->storage == REDIS_VM_MEMORY);
191
+
192
+ /* This is an important place where we can avoid copy-on-write
193
+ * when there is a saving child running, avoiding touching the
194
+ * refcount field of the object if it's not needed.
195
+ *
196
+ * If the encoding is RAW and there is room in the static buffer
197
+ * we'll be able to send the object to the client without
198
+ * messing with its page. */
199
+ if (obj->encoding == REDIS_ENCODING_RAW) {
200
+ if (_addReplyToBuffer(c,obj->ptr,sdslen(obj->ptr)) != REDIS_OK)
201
+ _addReplyObjectToList(c,obj);
202
+ } else {
203
+ /* FIXME: convert the long into string and use _addReplyToBuffer()
204
+ * instead of calling getDecodedObject. As this place in the
205
+ * code is too performance critical. */
206
+ obj = getDecodedObject(obj);
207
+ if (_addReplyToBuffer(c,obj->ptr,sdslen(obj->ptr)) != REDIS_OK)
208
+ _addReplyObjectToList(c,obj);
209
+ decrRefCount(obj);
210
+ }
211
+ }
212
+
213
+ void addReplySds(redisClient *c, sds s) {
214
+ if (_installWriteEvent(c) != REDIS_OK) {
215
+ /* The caller expects the sds to be free'd. */
216
+ sdsfree(s);
217
+ return;
218
+ }
219
+ if (_addReplyToBuffer(c,s,sdslen(s)) == REDIS_OK) {
220
+ sdsfree(s);
221
+ } else {
222
+ /* This method free's the sds when it is no longer needed. */
223
+ _addReplySdsToList(c,s);
224
+ }
225
+ }
226
+
227
+ void addReplyString(redisClient *c, char *s, size_t len) {
228
+ if (_installWriteEvent(c) != REDIS_OK) return;
229
+ if (_addReplyToBuffer(c,s,len) != REDIS_OK)
230
+ _addReplyStringToList(c,s,len);
231
+ }
232
+
233
+ void _addReplyError(redisClient *c, char *s, size_t len) {
234
+ addReplyString(c,"-ERR ",5);
235
+ addReplyString(c,s,len);
236
+ addReplyString(c,"\r\n",2);
237
+ }
238
+
239
+ void addReplyError(redisClient *c, char *err) {
240
+ _addReplyError(c,err,strlen(err));
241
+ }
242
+
243
+ void addReplyErrorFormat(redisClient *c, const char *fmt, ...) {
244
+ va_list ap;
245
+ va_start(ap,fmt);
246
+ sds s = sdscatvprintf(sdsempty(),fmt,ap);
247
+ va_end(ap);
248
+ _addReplyError(c,s,sdslen(s));
249
+ sdsfree(s);
250
+ }
251
+
252
+ void _addReplyStatus(redisClient *c, char *s, size_t len) {
253
+ addReplyString(c,"+",1);
254
+ addReplyString(c,s,len);
255
+ addReplyString(c,"\r\n",2);
256
+ }
257
+
258
+ void addReplyStatus(redisClient *c, char *status) {
259
+ _addReplyStatus(c,status,strlen(status));
260
+ }
261
+
262
+ void addReplyStatusFormat(redisClient *c, const char *fmt, ...) {
263
+ va_list ap;
264
+ va_start(ap,fmt);
265
+ sds s = sdscatvprintf(sdsempty(),fmt,ap);
266
+ va_end(ap);
267
+ _addReplyStatus(c,s,sdslen(s));
268
+ sdsfree(s);
269
+ }
270
+
271
+ /* Adds an empty object to the reply list that will contain the multi bulk
272
+ * length, which is not known when this function is called. */
273
+ void *addDeferredMultiBulkLength(redisClient *c) {
274
+ /* Note that we install the write event here even if the object is not
275
+ * ready to be sent, since we are sure that before returning to the
276
+ * event loop setDeferredMultiBulkLength() will be called. */
277
+ if (_installWriteEvent(c) != REDIS_OK) return NULL;
278
+ listAddNodeTail(c->reply,createObject(REDIS_STRING,NULL));
279
+ return listLast(c->reply);
280
+ }
281
+
282
+ /* Populate the length object and try glueing it to the next chunk. */
283
+ void setDeferredMultiBulkLength(redisClient *c, void *node, long length) {
284
+ listNode *ln = (listNode*)node;
285
+ robj *len, *next;
286
+
287
+ /* Abort when *node is NULL (see addDeferredMultiBulkLength). */
288
+ if (node == NULL) return;
289
+
290
+ len = listNodeValue(ln);
291
+ len->ptr = sdscatprintf(sdsempty(),"*%ld\r\n",length);
292
+ if (ln->next != NULL) {
293
+ next = listNodeValue(ln->next);
294
+
295
+ /* Only glue when the next node is non-NULL (an sds in this case) */
296
+ if (next->ptr != NULL) {
297
+ len->ptr = sdscatlen(len->ptr,next->ptr,sdslen(next->ptr));
298
+ listDelNode(c->reply,ln->next);
299
+ }
300
+ }
301
+ }
302
+
303
+ /* Add a duble as a bulk reply */
304
+ void addReplyDouble(redisClient *c, double d) {
305
+ char dbuf[128], sbuf[128];
306
+ int dlen, slen;
307
+ dlen = snprintf(dbuf,sizeof(dbuf),"%.17g",d);
308
+ slen = snprintf(sbuf,sizeof(sbuf),"$%d\r\n%s\r\n",dlen,dbuf);
309
+ addReplyString(c,sbuf,slen);
310
+ }
311
+
312
+ /* Add a long long as integer reply or bulk len / multi bulk count.
313
+ * Basically this is used to output <prefix><long long><crlf>. */
314
+ void _addReplyLongLong(redisClient *c, long long ll, char prefix) {
315
+ char buf[128];
316
+ int len;
317
+ buf[0] = prefix;
318
+ len = ll2string(buf+1,sizeof(buf)-1,ll);
319
+ buf[len+1] = '\r';
320
+ buf[len+2] = '\n';
321
+ addReplyString(c,buf,len+3);
322
+ }
323
+
324
+ void addReplyLongLong(redisClient *c, long long ll) {
325
+ _addReplyLongLong(c,ll,':');
326
+ }
327
+
328
+ void addReplyMultiBulkLen(redisClient *c, long length) {
329
+ _addReplyLongLong(c,length,'*');
330
+ }
331
+
332
+ /* Create the length prefix of a bulk reply, example: $2234 */
333
+ void addReplyBulkLen(redisClient *c, robj *obj) {
334
+ size_t len;
335
+
336
+ if (obj->encoding == REDIS_ENCODING_RAW) {
337
+ len = sdslen(obj->ptr);
338
+ } else {
339
+ long n = (long)obj->ptr;
340
+
341
+ /* Compute how many bytes will take this integer as a radix 10 string */
342
+ len = 1;
343
+ if (n < 0) {
344
+ len++;
345
+ n = -n;
346
+ }
347
+ while((n = n/10) != 0) {
348
+ len++;
349
+ }
350
+ }
351
+ _addReplyLongLong(c,len,'$');
352
+ }
353
+
354
+ /* Add a Redis Object as a bulk reply */
355
+ void addReplyBulk(redisClient *c, robj *obj) {
356
+ addReplyBulkLen(c,obj);
357
+ addReply(c,obj);
358
+ addReply(c,shared.crlf);
359
+ }
360
+
361
+ /* Add a C buffer as bulk reply */
362
+ void addReplyBulkCBuffer(redisClient *c, void *p, size_t len) {
363
+ _addReplyLongLong(c,len,'$');
364
+ addReplyString(c,p,len);
365
+ addReply(c,shared.crlf);
366
+ }
367
+
368
+ /* Add a C nul term string as bulk reply */
369
+ void addReplyBulkCString(redisClient *c, char *s) {
370
+ if (s == NULL) {
371
+ addReply(c,shared.nullbulk);
372
+ } else {
373
+ addReplyBulkCBuffer(c,s,strlen(s));
374
+ }
375
+ }
376
+
377
+ /* Add a long long as a bulk reply */
378
+ void addReplyBulkLongLong(redisClient *c, long long ll) {
379
+ char buf[64];
380
+ int len;
381
+
382
+ len = ll2string(buf,64,ll);
383
+ addReplyBulkCBuffer(c,buf,len);
384
+ }
385
+
386
+ static void acceptCommonHandler(int fd) {
387
+ redisClient *c;
388
+ if ((c = createClient(fd)) == NULL) {
389
+ redisLog(REDIS_WARNING,"Error allocating resoures for the client");
390
+ close(fd); /* May be already closed, just ingore errors */
391
+ return;
392
+ }
393
+ /* If maxclient directive is set and this is one client more... close the
394
+ * connection. Note that we create the client instead to check before
395
+ * for this condition, since now the socket is already set in nonblocking
396
+ * mode and we can send an error for free using the Kernel I/O */
397
+ if (server.maxclients && listLength(server.clients) > server.maxclients) {
398
+ char *err = "-ERR max number of clients reached\r\n";
399
+
400
+ /* That's a best effort error message, don't check write errors */
401
+ if (write(c->fd,err,strlen(err)) == -1) {
402
+ /* Nothing to do, Just to avoid the warning... */
403
+ }
404
+ freeClient(c);
405
+ return;
406
+ }
407
+ server.stat_numconnections++;
408
+ }
409
+
410
+ void acceptTcpHandler(aeEventLoop *el, int fd, void *privdata, int mask) {
411
+ int cport, cfd;
412
+ char cip[128];
413
+ REDIS_NOTUSED(el);
414
+ REDIS_NOTUSED(mask);
415
+ REDIS_NOTUSED(privdata);
416
+
417
+ cfd = anetTcpAccept(server.neterr, fd, cip, &cport);
418
+ if (cfd == AE_ERR) {
419
+ redisLog(REDIS_WARNING,"Accepting client connection: %s", server.neterr);
420
+ return;
421
+ }
422
+ redisLog(REDIS_VERBOSE,"Accepted %s:%d", cip, cport);
423
+ acceptCommonHandler(cfd);
424
+ }
425
+
426
+ void acceptUnixHandler(aeEventLoop *el, int fd, void *privdata, int mask) {
427
+ int cfd;
428
+ REDIS_NOTUSED(el);
429
+ REDIS_NOTUSED(mask);
430
+ REDIS_NOTUSED(privdata);
431
+
432
+ cfd = anetUnixAccept(server.neterr, fd);
433
+ if (cfd == AE_ERR) {
434
+ redisLog(REDIS_WARNING,"Accepting client connection: %s", server.neterr);
435
+ return;
436
+ }
437
+ redisLog(REDIS_VERBOSE,"Accepted connection to %s", server.unixsocket);
438
+ acceptCommonHandler(cfd);
439
+ }
440
+
441
+
442
+ static void freeClientArgv(redisClient *c) {
443
+ int j;
444
+ for (j = 0; j < c->argc; j++)
445
+ decrRefCount(c->argv[j]);
446
+ c->argc = 0;
447
+ }
448
+
449
+ void freeClient(redisClient *c) {
450
+ listNode *ln;
451
+
452
+ /* Note that if the client we are freeing is blocked into a blocking
453
+ * call, we have to set querybuf to NULL *before* to call
454
+ * unblockClientWaitingData() to avoid processInputBuffer() will get
455
+ * called. Also it is important to remove the file events after
456
+ * this, because this call adds the READABLE event. */
457
+ sdsfree(c->querybuf);
458
+ c->querybuf = NULL;
459
+ if (c->flags & REDIS_BLOCKED)
460
+ unblockClientWaitingData(c);
461
+
462
+ /* UNWATCH all the keys */
463
+ unwatchAllKeys(c);
464
+ listRelease(c->watched_keys);
465
+ /* Unsubscribe from all the pubsub channels */
466
+ pubsubUnsubscribeAllChannels(c,0);
467
+ pubsubUnsubscribeAllPatterns(c,0);
468
+ dictRelease(c->pubsub_channels);
469
+ listRelease(c->pubsub_patterns);
470
+ /* Obvious cleanup */
471
+ aeDeleteFileEvent(server.el,c->fd,AE_READABLE);
472
+ aeDeleteFileEvent(server.el,c->fd,AE_WRITABLE);
473
+ listRelease(c->reply);
474
+ freeClientArgv(c);
475
+ close(c->fd);
476
+ /* Remove from the list of clients */
477
+ ln = listSearchKey(server.clients,c);
478
+ redisAssert(ln != NULL);
479
+ listDelNode(server.clients,ln);
480
+ /* When client was just unblocked because of a blocking operation,
481
+ * remove it from the list with unblocked clients. */
482
+ if (c->flags & REDIS_UNBLOCKED) {
483
+ ln = listSearchKey(server.unblocked_clients,c);
484
+ redisAssert(ln != NULL);
485
+ listDelNode(server.unblocked_clients,ln);
486
+ }
487
+ /* Remove from the list of clients waiting for swapped keys, or ready
488
+ * to be restarted, but not yet woken up again. */
489
+ if (c->flags & REDIS_IO_WAIT) {
490
+ redisAssert(server.vm_enabled);
491
+ if (listLength(c->io_keys) == 0) {
492
+ ln = listSearchKey(server.io_ready_clients,c);
493
+
494
+ /* When this client is waiting to be woken up (REDIS_IO_WAIT),
495
+ * it should be present in the list io_ready_clients */
496
+ redisAssert(ln != NULL);
497
+ listDelNode(server.io_ready_clients,ln);
498
+ } else {
499
+ while (listLength(c->io_keys)) {
500
+ ln = listFirst(c->io_keys);
501
+ dontWaitForSwappedKey(c,ln->value);
502
+ }
503
+ }
504
+ server.vm_blocked_clients--;
505
+ }
506
+ listRelease(c->io_keys);
507
+ /* Master/slave cleanup.
508
+ * Case 1: we lost the connection with a slave. */
509
+ if (c->flags & REDIS_SLAVE) {
510
+ if (c->replstate == REDIS_REPL_SEND_BULK && c->repldbfd != -1)
511
+ close(c->repldbfd);
512
+ list *l = (c->flags & REDIS_MONITOR) ? server.monitors : server.slaves;
513
+ ln = listSearchKey(l,c);
514
+ redisAssert(ln != NULL);
515
+ listDelNode(l,ln);
516
+ }
517
+
518
+ /* Case 2: we lost the connection with the master. */
519
+ if (c->flags & REDIS_MASTER) {
520
+ server.master = NULL;
521
+ server.replstate = REDIS_REPL_CONNECT;
522
+ /* Since we lost the connection with the master, we should also
523
+ * close the connection with all our slaves if we have any, so
524
+ * when we'll resync with the master the other slaves will sync again
525
+ * with us as well. Note that also when the slave is not connected
526
+ * to the master it will keep refusing connections by other slaves.
527
+ *
528
+ * We do this only if server.masterhost != NULL. If it is NULL this
529
+ * means the user called SLAVEOF NO ONE and we are freeing our
530
+ * link with the master, so no need to close link with slaves. */
531
+ if (server.masterhost != NULL) {
532
+ while (listLength(server.slaves)) {
533
+ ln = listFirst(server.slaves);
534
+ freeClient((redisClient*)ln->value);
535
+ }
536
+ }
537
+ }
538
+ /* Release memory */
539
+ zfree(c->argv);
540
+ freeClientMultiState(c);
541
+ zfree(c);
542
+ }
543
+
544
+ void sendReplyToClient(aeEventLoop *el, int fd, void *privdata, int mask) {
545
+ redisClient *c = privdata;
546
+ int nwritten = 0, totwritten = 0, objlen;
547
+ robj *o;
548
+ REDIS_NOTUSED(el);
549
+ REDIS_NOTUSED(mask);
550
+
551
+ while(c->bufpos > 0 || listLength(c->reply)) {
552
+ if (c->bufpos > 0) {
553
+ if (c->flags & REDIS_MASTER) {
554
+ /* Don't reply to a master */
555
+ nwritten = c->bufpos - c->sentlen;
556
+ } else {
557
+ nwritten = write(fd,c->buf+c->sentlen,c->bufpos-c->sentlen);
558
+ if (nwritten <= 0) break;
559
+ }
560
+ c->sentlen += nwritten;
561
+ totwritten += nwritten;
562
+
563
+ /* If the buffer was sent, set bufpos to zero to continue with
564
+ * the remainder of the reply. */
565
+ if (c->sentlen == c->bufpos) {
566
+ c->bufpos = 0;
567
+ c->sentlen = 0;
568
+ }
569
+ } else {
570
+ o = listNodeValue(listFirst(c->reply));
571
+ objlen = sdslen(o->ptr);
572
+
573
+ if (objlen == 0) {
574
+ listDelNode(c->reply,listFirst(c->reply));
575
+ continue;
576
+ }
577
+
578
+ if (c->flags & REDIS_MASTER) {
579
+ /* Don't reply to a master */
580
+ nwritten = objlen - c->sentlen;
581
+ } else {
582
+ nwritten = write(fd, ((char*)o->ptr)+c->sentlen,objlen-c->sentlen);
583
+ if (nwritten <= 0) break;
584
+ }
585
+ c->sentlen += nwritten;
586
+ totwritten += nwritten;
587
+
588
+ /* If we fully sent the object on head go to the next one */
589
+ if (c->sentlen == objlen) {
590
+ listDelNode(c->reply,listFirst(c->reply));
591
+ c->sentlen = 0;
592
+ }
593
+ }
594
+ /* Note that we avoid to send more thank REDIS_MAX_WRITE_PER_EVENT
595
+ * bytes, in a single threaded server it's a good idea to serve
596
+ * other clients as well, even if a very large request comes from
597
+ * super fast link that is always able to accept data (in real world
598
+ * scenario think about 'KEYS *' against the loopback interfae) */
599
+ if (totwritten > REDIS_MAX_WRITE_PER_EVENT) break;
600
+ }
601
+ if (nwritten == -1) {
602
+ if (errno == EAGAIN) {
603
+ nwritten = 0;
604
+ } else {
605
+ redisLog(REDIS_VERBOSE,
606
+ "Error writing to client: %s", strerror(errno));
607
+ freeClient(c);
608
+ return;
609
+ }
610
+ }
611
+ if (totwritten > 0) c->lastinteraction = time(NULL);
612
+ if (listLength(c->reply) == 0) {
613
+ c->sentlen = 0;
614
+ aeDeleteFileEvent(server.el,c->fd,AE_WRITABLE);
615
+
616
+ /* Close connection after entire reply has been sent. */
617
+ if (c->flags & REDIS_CLOSE_AFTER_REPLY) freeClient(c);
618
+ }
619
+ }
620
+
621
+ /* resetClient prepare the client to process the next command */
622
+ void resetClient(redisClient *c) {
623
+ freeClientArgv(c);
624
+ c->reqtype = 0;
625
+ c->multibulklen = 0;
626
+ c->bulklen = -1;
627
+ }
628
+
629
+ void closeTimedoutClients(void) {
630
+ redisClient *c;
631
+ listNode *ln;
632
+ time_t now = time(NULL);
633
+ listIter li;
634
+
635
+ listRewind(server.clients,&li);
636
+ while ((ln = listNext(&li)) != NULL) {
637
+ c = listNodeValue(ln);
638
+ if (server.maxidletime &&
639
+ !(c->flags & REDIS_SLAVE) && /* no timeout for slaves */
640
+ !(c->flags & REDIS_MASTER) && /* no timeout for masters */
641
+ !(c->flags & REDIS_BLOCKED) && /* no timeout for BLPOP */
642
+ dictSize(c->pubsub_channels) == 0 && /* no timeout for pubsub */
643
+ listLength(c->pubsub_patterns) == 0 &&
644
+ (now - c->lastinteraction > server.maxidletime))
645
+ {
646
+ redisLog(REDIS_VERBOSE,"Closing idle client");
647
+ freeClient(c);
648
+ } else if (c->flags & REDIS_BLOCKED) {
649
+ if (c->bpop.timeout != 0 && c->bpop.timeout < now) {
650
+ addReply(c,shared.nullmultibulk);
651
+ unblockClientWaitingData(c);
652
+ }
653
+ }
654
+ }
655
+ }
656
+
657
+ int processInlineBuffer(redisClient *c) {
658
+ char *newline = strstr(c->querybuf,"\r\n");
659
+ int argc, j;
660
+ sds *argv;
661
+ size_t querylen;
662
+
663
+ /* Nothing to do without a \r\n */
664
+ if (newline == NULL)
665
+ return REDIS_ERR;
666
+
667
+ /* Split the input buffer up to the \r\n */
668
+ querylen = newline-(c->querybuf);
669
+ argv = sdssplitlen(c->querybuf,querylen," ",1,&argc);
670
+
671
+ /* Leave data after the first line of the query in the buffer */
672
+ c->querybuf = sdsrange(c->querybuf,querylen+2,-1);
673
+
674
+ /* Setup argv array on client structure */
675
+ if (c->argv) zfree(c->argv);
676
+ c->argv = zmalloc(sizeof(robj*)*argc);
677
+
678
+ /* Create redis objects for all arguments. */
679
+ for (c->argc = 0, j = 0; j < argc; j++) {
680
+ if (sdslen(argv[j])) {
681
+ c->argv[c->argc] = createObject(REDIS_STRING,argv[j]);
682
+ c->argc++;
683
+ } else {
684
+ sdsfree(argv[j]);
685
+ }
686
+ }
687
+ zfree(argv);
688
+ return REDIS_OK;
689
+ }
690
+
691
+ /* Helper function. Trims query buffer to make the function that processes
692
+ * multi bulk requests idempotent. */
693
+ static void setProtocolError(redisClient *c, int pos) {
694
+ c->flags |= REDIS_CLOSE_AFTER_REPLY;
695
+ c->querybuf = sdsrange(c->querybuf,pos,-1);
696
+ }
697
+
698
+ int processMultibulkBuffer(redisClient *c) {
699
+ char *newline = NULL;
700
+ char *eptr;
701
+ int pos = 0, tolerr;
702
+ long bulklen;
703
+
704
+ if (c->multibulklen == 0) {
705
+ /* The client should have been reset */
706
+ redisAssert(c->argc == 0);
707
+
708
+ /* Multi bulk length cannot be read without a \r\n */
709
+ newline = strstr(c->querybuf,"\r\n");
710
+ if (newline == NULL)
711
+ return REDIS_ERR;
712
+
713
+ /* We know for sure there is a whole line since newline != NULL,
714
+ * so go ahead and find out the multi bulk length. */
715
+ redisAssert(c->querybuf[0] == '*');
716
+ c->multibulklen = strtol(c->querybuf+1,&eptr,10);
717
+ pos = (newline-c->querybuf)+2;
718
+ if (c->multibulklen <= 0) {
719
+ c->querybuf = sdsrange(c->querybuf,pos,-1);
720
+ return REDIS_OK;
721
+ } else if (c->multibulklen > 1024*1024) {
722
+ addReplyError(c,"Protocol error: invalid multibulk length");
723
+ setProtocolError(c,pos);
724
+ return REDIS_ERR;
725
+ }
726
+
727
+ /* Setup argv array on client structure */
728
+ if (c->argv) zfree(c->argv);
729
+ c->argv = zmalloc(sizeof(robj*)*c->multibulklen);
730
+
731
+ /* Search new newline */
732
+ newline = strstr(c->querybuf+pos,"\r\n");
733
+ }
734
+
735
+ redisAssert(c->multibulklen > 0);
736
+ while(c->multibulklen) {
737
+ /* Read bulk length if unknown */
738
+ if (c->bulklen == -1) {
739
+ newline = strstr(c->querybuf+pos,"\r\n");
740
+ if (newline != NULL) {
741
+ if (c->querybuf[pos] != '$') {
742
+ addReplyErrorFormat(c,
743
+ "Protocol error: expected '$', got '%c'",
744
+ c->querybuf[pos]);
745
+ setProtocolError(c,pos);
746
+ return REDIS_ERR;
747
+ }
748
+
749
+ bulklen = strtol(c->querybuf+pos+1,&eptr,10);
750
+ tolerr = (eptr[0] != '\r');
751
+ if (tolerr || bulklen == LONG_MIN || bulklen == LONG_MAX ||
752
+ bulklen < 0 || bulklen > 512*1024*1024)
753
+ {
754
+ addReplyError(c,"Protocol error: invalid bulk length");
755
+ setProtocolError(c,pos);
756
+ return REDIS_ERR;
757
+ }
758
+ pos += eptr-(c->querybuf+pos)+2;
759
+ c->bulklen = bulklen;
760
+ } else {
761
+ /* No newline in current buffer, so wait for more data */
762
+ break;
763
+ }
764
+ }
765
+
766
+ /* Read bulk argument */
767
+ if (sdslen(c->querybuf)-pos < (unsigned)(c->bulklen+2)) {
768
+ /* Not enough data (+2 == trailing \r\n) */
769
+ break;
770
+ } else {
771
+ c->argv[c->argc++] = createStringObject(c->querybuf+pos,c->bulklen);
772
+ pos += c->bulklen+2;
773
+ c->bulklen = -1;
774
+ c->multibulklen--;
775
+ }
776
+ }
777
+
778
+ /* Trim to pos */
779
+ c->querybuf = sdsrange(c->querybuf,pos,-1);
780
+
781
+ /* We're done when c->multibulk == 0 */
782
+ if (c->multibulklen == 0) {
783
+ return REDIS_OK;
784
+ }
785
+ return REDIS_ERR;
786
+ }
787
+
788
+ void processInputBuffer(redisClient *c) {
789
+ /* Keep processing while there is something in the input buffer */
790
+ while(sdslen(c->querybuf)) {
791
+ /* Immediately abort if the client is in the middle of something. */
792
+ if (c->flags & REDIS_BLOCKED || c->flags & REDIS_IO_WAIT) return;
793
+
794
+ /* REDIS_CLOSE_AFTER_REPLY closes the connection once the reply is
795
+ * written to the client. Make sure to not let the reply grow after
796
+ * this flag has been set (i.e. don't process more commands). */
797
+ if (c->flags & REDIS_CLOSE_AFTER_REPLY) return;
798
+
799
+ /* Determine request type when unknown. */
800
+ if (!c->reqtype) {
801
+ if (c->querybuf[0] == '*') {
802
+ c->reqtype = REDIS_REQ_MULTIBULK;
803
+ } else {
804
+ c->reqtype = REDIS_REQ_INLINE;
805
+ }
806
+ }
807
+
808
+ if (c->reqtype == REDIS_REQ_INLINE) {
809
+ if (processInlineBuffer(c) != REDIS_OK) break;
810
+ } else if (c->reqtype == REDIS_REQ_MULTIBULK) {
811
+ if (processMultibulkBuffer(c) != REDIS_OK) break;
812
+ } else {
813
+ redisPanic("Unknown request type");
814
+ }
815
+
816
+ /* Multibulk processing could see a <= 0 length. */
817
+ if (c->argc == 0) {
818
+ resetClient(c);
819
+ } else {
820
+ /* Only reset the client when the command was executed. */
821
+ if (processCommand(c) == REDIS_OK)
822
+ resetClient(c);
823
+ }
824
+ }
825
+ }
826
+
827
+ void readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask) {
828
+ redisClient *c = (redisClient*) privdata;
829
+ char buf[REDIS_IOBUF_LEN];
830
+ int nread;
831
+ REDIS_NOTUSED(el);
832
+ REDIS_NOTUSED(mask);
833
+
834
+ nread = read(fd, buf, REDIS_IOBUF_LEN);
835
+ if (nread == -1) {
836
+ if (errno == EAGAIN) {
837
+ nread = 0;
838
+ } else {
839
+ redisLog(REDIS_VERBOSE, "Reading from client: %s",strerror(errno));
840
+ freeClient(c);
841
+ return;
842
+ }
843
+ } else if (nread == 0) {
844
+ redisLog(REDIS_VERBOSE, "Client closed connection");
845
+ freeClient(c);
846
+ return;
847
+ }
848
+ if (nread) {
849
+ c->querybuf = sdscatlen(c->querybuf,buf,nread);
850
+ c->lastinteraction = time(NULL);
851
+ } else {
852
+ return;
853
+ }
854
+ processInputBuffer(c);
855
+ }
856
+
857
+ void getClientsMaxBuffers(unsigned long *longest_output_list,
858
+ unsigned long *biggest_input_buffer) {
859
+ redisClient *c;
860
+ listNode *ln;
861
+ listIter li;
862
+ unsigned long lol = 0, bib = 0;
863
+
864
+ listRewind(server.clients,&li);
865
+ while ((ln = listNext(&li)) != NULL) {
866
+ c = listNodeValue(ln);
867
+
868
+ if (listLength(c->reply) > lol) lol = listLength(c->reply);
869
+ if (sdslen(c->querybuf) > bib) bib = sdslen(c->querybuf);
870
+ }
871
+ *longest_output_list = lol;
872
+ *biggest_input_buffer = bib;
873
+ }
874
+
875
+ void rewriteClientCommandVector(redisClient *c, int argc, ...) {
876
+ va_list ap;
877
+ int j;
878
+ robj **argv; /* The new argument vector */
879
+
880
+ argv = zmalloc(sizeof(robj*)*argc);
881
+ va_start(ap,argc);
882
+ for (j = 0; j < argc; j++) {
883
+ robj *a;
884
+
885
+ a = va_arg(ap, robj*);
886
+ argv[j] = a;
887
+ incrRefCount(a);
888
+ }
889
+ /* We free the objects in the original vector at the end, so we are
890
+ * sure that if the same objects are reused in the new vector the
891
+ * refcount gets incremented before it gets decremented. */
892
+ for (j = 0; j < c->argc; j++) decrRefCount(c->argv[j]);
893
+ zfree(c->argv);
894
+ /* Replace argv and argc with our new versions. */
895
+ c->argv = argv;
896
+ c->argc = argc;
897
+ va_end(ap);
898
+ }
899
+