2 +--------------------------------------------------------------------+
3 | libmemcached - 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 Michael Wallner <mike@php.net> |
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"
45 static void add_file(std::vector
<memcp_file
> &files
, const client_options
&opt
, const char *file
) {
46 memcp_file::key type
= memcp_file::basename
;
47 memcp_file::op mode
= memcp_file::SET
;
51 if (opt
.isset("absolute")) {
52 type
= memcp_file::absolute
;
53 } else if (opt
.isset("relative")) {
54 type
= memcp_file::relative
;
57 if (opt
.isset("replace")) {
58 mode
= memcp_file::REPLACE
;
59 } else if (opt
.isset("add")) {
60 mode
= memcp_file::ADD
;
63 if (auto flags_str
= opt
.argof("flags")) {
64 flags
= std::stoul(flags_str
);
66 if (auto expire_str
= opt
.argof("expire")) {
67 expire
= std::stoul(expire_str
);
70 if (opt
.isset("debug")) {
71 auto mode_str
= mode
== memcp_file::REPLACE
? "REPLACE" : mode
== memcp_file::ADD
? "ADD" : "SET";
72 std::cerr
<< "Scheduling " << mode_str
<< " '" << file
<< "' (expire=" << expire
<< ", flags=" << flags
<< ").\n";
75 files
.emplace_back(memcp_file
{type
, mode
, file
, flags
, expire
});
78 int main(int argc
, char *argv
[]) {
80 std::vector
<memcp_file
> files
{};
81 client_options opt
{PROGRAM_NAME
, PROGRAM_VERSION
, PROGRAM_DESCRIPTION
, "file [file ...]"};
83 opt
.add(nullptr, '-', no_argument
, "GNU argv extension")
84 .parse
= [&files
](client_options
&opt_
, client_options::extended_option
&ext
) {
85 add_file(files
, opt_
, ext
.arg
);
89 for (const auto &def
: opt
.defaults
) {
93 opt
.add("set", 'S', no_argument
, "Perform SET operations.")
94 .parse
= [](client_options
&opt_
, client_options::extended_option
&) {
96 opt_
.unset("replace");
99 opt
.add("add", 'A', no_argument
, "Perform ADD operations.")
100 .parse
= [](client_options
&opt_
, client_options::extended_option
&) {
102 opt_
.unset("replace");
105 opt
.add("replace", 'R', no_argument
, "Perform REPLACE operations.")
106 .parse
= [](client_options
&opt_
, client_options::extended_option
&) {
112 opt
.add("udp", 'U', no_argument
, "Use UDP.")
113 .apply
= [](const client_options
&opt
, const client_options::extended_option
&ext
, memcached_st
*memc
) {
114 if (MEMCACHED_SUCCESS
!= memcached_behavior_set(memc
, MEMCACHED_BEHAVIOR_USE_UDP
, ext
.set
)) {
115 if (!opt
.isset("quiet")) {
116 std::cerr
<< memcached_last_error_message(memc
) << "\n";
122 opt
.add("flags", 'F', required_argument
, "Set key flags, too.");
123 opt
.add("expire", 'e', required_argument
, "Set expire time, too.");
125 opt
.add("basename", '.', no_argument
, "Use basename of path as key (default).");
126 opt
.add("relative", '+', no_argument
, "Use relative path (as passed), instead of basename only.");
127 opt
.add("absolute", '/', no_argument
, "Use absolute path (real path), instead of basename only.");
133 char **argp
= nullptr;
134 if (!opt
.parse(argc
, argv
, &argp
)) {
138 if (!memcached_create(&memc
)) {
139 if (!opt
.isset("quiet")) {
140 std::cerr
<< "Failed to initialize memcached client.\n";
145 if (!opt
.apply(&memc
)) {
151 if (!opt
.isset("quiet")) {
152 std::cerr
<< "No file(s) provided.\n";
154 memcached_free(&memc
);
157 for (auto arg
= argp
; *arg
; ++arg
) {
158 add_file(files
, opt
, *arg
);
162 auto exit_code
= EXIT_SUCCESS
;
163 for (const auto &file
: files
) {
164 auto filename
= file
.path
;
165 std::ifstream filestream
{filename
, std::ios::in
|std::ios::binary
};
168 if (!opt
.isset("quiet")) {
169 std::cerr
<< "Could not open file '" << filename
<< "'.\n";
171 exit_code
= EXIT_FAILURE
;
175 char rpath
[PATH_MAX
+1];
177 if (file
.type
== memcp_file::relative
) {
179 } else if (file
.type
== memcp_file::absolute
) {
180 path
= realpath(filename
, rpath
);
182 if (!opt
.isset("quiet")) {
185 exit_code
= EXIT_FAILURE
;
189 path
= basename(filename
);
192 std::ostringstream data
{};
193 data
<< filestream
.rdbuf();
195 memcached_return_t rc
;
197 if (file
.mode
== memcp_file::REPLACE
) {
199 rc
= memcached_replace(&memc
, path
, strlen(path
), data
.str().c_str(), data
.str().length(),
200 file
.expire
, file
.flags
);
201 } else if (file
.mode
== memcp_file::ADD
) {
203 rc
= memcached_add(&memc
, path
, strlen(path
), data
.str().c_str(), data
.str().length(),
204 file
.expire
, file
.flags
);
207 rc
= memcached_set(&memc
, path
, strlen(path
), data
.str().c_str(), data
.str().length(),
208 file
.expire
, file
.flags
);
211 if (MEMCACHED_SUCCESS
!= rc
) {
212 exit_code
= EXIT_FAILURE
;
214 auto error
= memcached_last_error(&memc
)
215 ? memcached_last_error_message(&memc
)
216 : memcached_strerror(&memc
, rc
);
217 std::cerr
<< "Error occurred during memcached_" << mode
<<"('" << path
<< "'): " << error
<< "\n";
221 if (opt
.isset("verbose")) {
222 std::cerr
<< path
<< "\n";
227 memcached_free(&memc
);