2 +--------------------------------------------------------------------+
3 | libmemcached-awesome - C/C++ Client Library for memcached |
4 +--------------------------------------------------------------------+
5 | Redistribution and use in source and binary forms, with or without |
6 | modification, are permitted under the terms of the BSD license. |
7 | You should have received a copy of the license in a bundled file |
8 | named LICENSE; in case you did not receive a copy you can review |
9 | the terms online at: https://opensource.org/licenses/BSD-3-Clause |
10 +--------------------------------------------------------------------+
11 | Copyright (c) 2006-2014 Brian Aker https://datadifferential.com/ |
12 | Copyright (c) 2020-2021 Michael Wallner https://awesome.co/ |
13 +--------------------------------------------------------------------+
16 #include "mem_config.h"
18 #define PROGRAM_NAME "memcp"
19 #define PROGRAM_DESCRIPTION "Copy a set of files to a memcached cluster."
20 #define PROGRAM_VERSION "1.1"
22 #include "common/options.hpp"
23 #include "common/checks.hpp"
24 #include "p9y/libgen.hpp"
25 #include "p9y/realpath.hpp"
35 # define PATH_MAX MAX_PATH
57 static inline std::string
stream2string(const std::istream
&istream
) {
58 return dynamic_cast<std::ostringstream
&&>(std::ostringstream
{} << istream
.rdbuf()).str();
61 static memcached_return_t
memcp(const client_options
&opt
, memcached_st
&memc
, const char *key
,
62 const memcp_file
&file
) {
63 std::ifstream fstream
{};
64 std::istream
*istream
= check_istream(opt
, file
.path
, fstream
);
67 return MEMCACHED_ERROR
;
71 memcached_return_t rc
;
72 auto data
= stream2string(*istream
);
73 if (file
.op
== memcp_file::mode::REPLACE
) {
75 rc
= memcached_replace(&memc
, key
, strlen(key
), data
.c_str(), data
.length(),
76 file
.expire
, file
.flags
);
77 } else if (file
.op
== memcp_file::mode::ADD
) {
79 rc
= memcached_add(&memc
, key
, strlen(key
), data
.c_str(), data
.length(),
80 file
.expire
, file
.flags
);
83 rc
= memcached_set(&memc
, key
, strlen(key
), data
.c_str(), data
.length(),
84 file
.expire
, file
.flags
);
87 if (!memcached_success(rc
)) {
88 auto error
= memcached_last_error(&memc
)
89 ? memcached_last_error_message(&memc
)
90 : memcached_strerror(&memc
, rc
);
91 std::cerr
<< "Error occurred during memcached_" << mode
<<"('" << key
<< "'): " << error
<< "\n";
96 static void add_file(std::vector
<memcp_file
> &files
, const client_options
&opt
, char *file
) {
97 memcp_file::type type
= memcp_file::type::basename
;
98 memcp_file::mode mode
= memcp_file::mode::SET
;
102 if (opt
.isset("absolute")) {
103 type
= memcp_file::type::absolute
;
104 } else if (opt
.isset("relative")) {
105 type
= memcp_file::type::relative
;
108 if (opt
.isset("replace")) {
109 mode
= memcp_file::mode::REPLACE
;
110 } else if (opt
.isset("add")) {
111 mode
= memcp_file::mode::ADD
;
114 if (auto flags_str
= opt
.argof("flags")) {
115 flags
= std::stoul(flags_str
);
117 if (auto expire_str
= opt
.argof("expire")) {
118 expire
= std::stoul(expire_str
);
121 if (opt
.isset("debug")) {
122 auto mode_str
= mode
== memcp_file::mode::REPLACE
? "REPLACE" : mode
== memcp_file::mode::ADD
? "ADD" : "SET";
123 std::cerr
<< "Scheduling " << mode_str
<< " '" << file
<< "' (expire=" << expire
<< ", flags=" << flags
<< ").\n";
126 files
.emplace_back(memcp_file
{type
, mode
, file
, flags
, expire
});
129 static bool path2key(const client_options
&opt
, memcp_file
&file
, char **path
) {
130 static char rpath
[PATH_MAX
+ 1];
131 if (file
.key
== memcp_file::type::absolute
) {
132 *path
= realpath(file
.path
, rpath
);
134 if (!opt
.isset("quiet")) {
139 } else if (file
.key
== memcp_file::type::relative
) {
142 *path
= basename(file
.path
);
147 int main(int argc
, char *argv
[]) {
148 std::vector
<memcp_file
> files
{};
149 client_options opt
{PROGRAM_NAME
, PROGRAM_VERSION
, PROGRAM_DESCRIPTION
,
151 "\n\t\t\t# NOTE: order of flags and positional"
152 "\n\t\t\t# arguments matters on GNU systems"};
154 opt
.add(nullptr, '-', no_argument
, "GNU argv extension")
155 .parse
= [&files
](client_options
&opt_
, client_options::extended_option
&ext
) {
156 add_file(files
, opt_
, ext
.arg
);
160 for (const auto &def
: opt
.defaults
) {
164 opt
.add("set", 'S', no_argument
, "Perform SET operations.")
165 .parse
= [](client_options
&opt_
, client_options::extended_option
&) {
167 opt_
.unset("replace");
170 opt
.add("add", 'A', no_argument
, "Perform ADD operations.")
171 .parse
= [](client_options
&opt_
, client_options::extended_option
&) {
173 opt_
.unset("replace");
176 opt
.add("replace", 'R', no_argument
, "Perform REPLACE operations.")
177 .parse
= [](client_options
&opt_
, client_options::extended_option
&) {
183 opt
.add("udp", 'U', no_argument
, "Use UDP.")
184 .apply
= [](const client_options
&opt_
, const client_options::extended_option
&ext
, memcached_st
*memc
) {
185 if (MEMCACHED_SUCCESS
!= memcached_behavior_set(memc
, MEMCACHED_BEHAVIOR_USE_UDP
, ext
.set
)) {
186 if (!opt_
.isset("quiet")) {
187 std::cerr
<< memcached_last_error_message(memc
) << "\n";
193 opt
.add("flags", 'F', required_argument
, "Set key flags, too.");
194 opt
.add("expire", 'e', required_argument
, "Set expire time, too.");
196 opt
.add("basename", '.', no_argument
, "Use basename of path as key (default).");
197 opt
.add("relative", '+', no_argument
, "Use relative path (as passed), instead of basename only.");
198 opt
.add("absolute", '/', no_argument
, "Use absolute path (real path), instead of basename only.");
204 char **argp
= nullptr;
205 if (!opt
.parse(argc
, argv
, &argp
)) {
210 if (!check_memcached(opt
, memc
)) {
214 if (!opt
.apply(&memc
)) {
219 if (!check_argp(opt
, argp
, "No file(s) provided.")) {
220 memcached_free(&memc
);
223 for (auto arg
= argp
; *arg
; ++arg
) {
224 add_file(files
, opt
, *arg
);
228 auto exit_code
= EXIT_SUCCESS
;
229 for (auto &file
: files
) {
230 char *path
= nullptr;
231 if (!path2key(opt
, file
, &path
)) {
232 exit_code
= EXIT_FAILURE
;
236 auto rc
= memcp(opt
, memc
, path
, file
);
237 if (memcached_success(rc
)) {
238 if (opt
.isset("verbose")) {
239 std::cout
<< path
<< "\n";
242 exit_code
= EXIT_FAILURE
;
246 if (!check_buffering(opt
, memc
)) {
247 exit_code
= EXIT_FAILURE
;
250 memcached_free(&memc
);