blob: 987502467057f6b6c3a574cd60de830d43b72238 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
#include "kudu/util/path_util.h"
// Use the POSIX version of basename(3)/dirname(3).
#include <libgen.h>
#if defined(__APPLE__)
#include <sys/param.h> // for MAXPATHLEN
#endif // defined(__APPLE__)
#include <cstdlib>
#include <cstring>
#include <memory>
#include <ostream>
#include <string>
#if defined(__APPLE__)
#include <cerrno>
#endif // defined(__APPLE__)
#include <glog/logging.h>
#include "kudu/gutil/strings/split.h"
#include "kudu/gutil/strings/stringpiece.h"
#include "kudu/gutil/strings/strip.h"
#include "kudu/util/env.h"
#include "kudu/util/status.h"
#include "kudu/util/subprocess.h"
#if defined(__APPLE__)
#include "kudu/gutil/port.h"
#include "kudu/gutil/strings/substitute.h"
#include "kudu/util/errno.h"
#endif // defined(__APPLE__)
using std::string;
using std::unique_ptr;
using std::vector;
using strings::SkipEmpty;
using strings::Split;
namespace kudu {
namespace {
struct FreeDeleter {
inline void operator()(void* ptr) const {
free(ptr);
}
};
} // anonymous namespace
const char kTmpInfix[] = ".kudutmp";
const char kOldTmpInfix[] = ".tmp";
string JoinPathSegments(const string& a, const string& b) {
CHECK(!a.empty()) << "empty first component: " << a;
CHECK(!b.empty() && b[0] != '/')
<< "second path component must be non-empty and relative: "
<< b;
if (a[a.size() - 1] == '/') {
return a + b;
} else {
return a + "/" + b;
}
}
vector<string> JoinPathSegmentsV(const vector<string>& v, const string& s) {
vector<string> out;
for (const string& path : v) {
out.emplace_back(JoinPathSegments(path, s));
}
return out;
}
vector<string> SplitPath(const string& path) {
if (path.empty()) return {};
vector<string> segments;
if (path[0] == '/') segments.emplace_back("/");
vector<StringPiece> pieces = Split(path, "/", SkipEmpty());
for (const StringPiece& piece : pieces) {
segments.emplace_back(piece.data(), piece.size());
}
return segments;
}
string DirName(const string& path) {
#if defined(__APPLE__)
char buf[MAXPATHLEN];
auto* ret = dirname_r(path.c_str(), buf);
if (PREDICT_FALSE(ret == nullptr)) {
int err = errno;
LOG(FATAL) << strings::Substitute("dirname_r() failed: $0",
ErrnoToString(err));
}
return ret;
#else
unique_ptr<char[], FreeDeleter> path_copy(strdup(path.c_str()));
return dirname(path_copy.get());
#endif // #if defined(__APPLE__) ... #else
}
string BaseName(const string& path) {
#if defined(__APPLE__)
char buf[MAXPATHLEN];
auto* ret = basename_r(path.c_str(), buf);
if (PREDICT_FALSE(ret == nullptr)) {
int err = errno;
LOG(FATAL) << strings::Substitute("basename_r() failed: $0",
ErrnoToString(err));
}
return ret;
#else
unique_ptr<char[], FreeDeleter> path_copy(strdup(path.c_str()));
return basename(path_copy.get());
#endif // #if defined(__APPLE__) ... #else
}
Status FindExecutable(const string& binary,
const vector<string>& search,
string* path) {
string p;
// First, check specified locations. This is necessary to check first so that
// the system binaries won't be found before the specified search locations.
for (const auto& location : search) {
p = JoinPathSegments(location, binary);
if (Env::Default()->FileExists(p)) {
*path = p;
return Status::OK();
}
}
// Next check if the binary is on the PATH.
Status s = Subprocess::Call({ "which", binary }, "", &p);
if (s.ok()) {
StripTrailingNewline(&p);
*path = p;
return Status::OK();
}
return Status::NotFound("Unable to find binary", binary);
}
} // namespace kudu