Fcitx
library.cpp
1 /*
2  * SPDX-FileCopyrightText: 2016-2016 CSSlayer <wengxt@gmail.com>
3  *
4  * SPDX-License-Identifier: LGPL-2.1-or-later
5  *
6  */
7 
8 #include "library.h"
9 #include <dlfcn.h>
10 #include <fcntl.h>
11 #include <sys/stat.h>
12 #include <unistd.h>
13 #include <cerrno>
14 #include <cstdlib>
15 #include <cstring>
16 #include <filesystem>
17 #include <functional>
18 #include <memory>
19 #include <string>
20 #include <utility>
21 #include "config.h"
22 #include "flags.h"
23 #include "macros.h"
24 #include "misc.h"
25 #include "standardpaths.h"
26 #include "stringutils.h"
27 #include "unixfd.h"
28 
29 #ifdef HAVE_SYS_MMAN_H
30 #include <sys/mman.h>
31 #endif
32 
33 namespace fcitx {
34 
35 namespace {
36 
37 #ifdef RTLD_NODELETE
38 constexpr bool hasRTLDNoDelete = true;
39 #else
40 constexpr bool hasRTLDNoDelete = false;
41 #endif
42 
43 } // namespace
44 
46 public:
47  LibraryPrivate(std::filesystem::path path)
48  : path_(std::move(path)), pathStr_(path_.string()) {}
49  ~LibraryPrivate() { unload(); }
50 
51  bool unload() {
52  if (!handle_) {
53  return false;
54  }
55 
56  // If there is no RTLD_NODELETE and we don't want to unload, we leak
57  // it on purpose.
58  if (hasRTLDNoDelete ||
59  !loadFlag_.test(LibraryLoadHint::PreventUnloadHint)) {
60  if (dlclose(handle_)) {
61  error_ = dlerror();
62  return false;
63  }
64  }
65 
66  handle_ = nullptr;
67  return true;
68  }
69  const std::filesystem::path path_;
70  const std::string pathStr_;
71  void *handle_ = nullptr;
72  std::string error_;
74 };
75 
76 Library::Library(const std::string &path)
77  : Library(std::filesystem::path(path)) {}
78 
79 Library::Library(const char *path) : Library(std::filesystem::path(path)) {}
80 
81 Library::Library(const std::filesystem::path &path)
82  : d_ptr(std::make_unique<LibraryPrivate>(path)) {}
83 
84 FCITX_DEFINE_DEFAULT_DTOR_AND_MOVE(Library)
85 
86 bool Library::load(Flags<fcitx::LibraryLoadHint> hint) {
87  if (loaded()) {
88  return true;
89  }
90  FCITX_D();
91  int flag = 0;
92  if (hint & LibraryLoadHint::ResolveAllSymbolsHint) {
93  flag |= RTLD_NOW;
94  } else {
95  flag |= RTLD_LAZY;
96  }
97 
98 #ifdef RTLD_NODELETE
99  if (hint & LibraryLoadHint::PreventUnloadHint) {
100  flag |= RTLD_NODELETE;
101  }
102 #endif
103 
104  if (hint & LibraryLoadHint::ExportExternalSymbolsHint) {
105  flag |= RTLD_GLOBAL;
106  }
107 
108 #ifdef HAS_DLMOPEN
109  if (hint & LibraryLoadHint::NewNameSpace) {
110  // allow dlopen self
111  d->handle_ = dlmopen(
112  LM_ID_NEWLM,
113  !d->path_.empty() ? d->path_.string().c_str() : nullptr, flag);
114  } else
115 #endif
116  {
117  d->handle_ = dlopen(
118  !d->path_.empty() ? d->path_.string().c_str() : nullptr, flag);
119  }
120  if (!d->handle_) {
121  d->error_ = dlerror();
122  return false;
123  }
124 
125  d->loadFlag_ = hint;
126 
127  return true;
128 }
129 
130 bool Library::loaded() const {
131  FCITX_D();
132  return !!d->handle_;
133 }
134 
135 bool Library::unload() {
136  FCITX_D();
137  return d->unload();
138 }
139 
140 void *Library::resolve(const char *name) {
141  FCITX_D();
142  auto *result = dlsym(d->handle_, name);
143  if (!result) {
144  d->error_ = dlerror();
145  }
146  return result;
147 }
148 
149 bool Library::findData(const char *slug, const char *magic, size_t lenOfMagic,
150  const std::function<void(const char *data)> &parser) {
151  FCITX_D();
152  if (d->handle_) {
153  void *data = dlsym(d->handle_, slug);
154  if (!data) {
155  return false;
156  }
157 
158  if (memcmp(data, magic, lenOfMagic) != 0) {
159  return false;
160  }
161 
162  data = static_cast<char *>(data) + lenOfMagic;
163  parser(static_cast<const char *>(data));
164  return true;
165  }
166 
167  UnixFD fd = StandardPaths::openPath(d->path_);
168  if (!fd.isValid()) {
169  d->error_ = strerror(errno);
170  return false;
171  }
172 
173  UniqueCPtr<void> needfree;
174  bool result = false;
175  do {
176  struct stat statbuf;
177  int statresult = fstat(fd.fd(), &statbuf);
178  if (statresult < 0) {
179  d->error_ = strerror(errno);
180  break;
181  }
182  void *data = nullptr;
183 #ifdef HAVE_SYS_MMAN_H
184  data =
185  mmap(nullptr, statbuf.st_size, PROT_READ, MAP_PRIVATE, fd.fd(), 0);
186  void *needunmap = nullptr;
187  needunmap = data;
188 #endif
189  if (!data) {
190  data = malloc(statbuf.st_size);
191  needfree.reset(data);
192  if (!data) {
193  break;
194  }
195  if (read(fd.fd(), data, statbuf.st_size) != statbuf.st_size) {
196  break;
197  }
198  }
199  const char *pos = stringutils::backwardSearch(
200  static_cast<char *>(data), static_cast<size_t>(statbuf.st_size),
201  magic, lenOfMagic, 0);
202  pos += lenOfMagic;
203 
204  if (parser) {
205  parser(pos);
206  }
207  result = true;
208 #ifdef HAVE_SYS_MMAN_H
209  if (needunmap) {
210  munmap(needunmap, statbuf.st_size);
211  }
212 #endif
213  } while (false);
214 
215  return result;
216 }
217 
218 std::string Library::error() {
219  FCITX_D();
220  return d->error_;
221 }
222 
223 bool Library::isNewNamespaceSupported() {
224 #ifdef HAS_DLMOPEN
225  return true;
226 #else
227  return false;
228 #endif
229 }
230 
231 const std::string &Library::path() const {
232  FCITX_D();
233  return d->pathStr_;
234 }
235 
236 const std::filesystem::path &Library::fspath() const {
237  FCITX_D();
238  return d->path_;
239 }
240 } // namespace fcitx
Class wrap around the unix fd.
Definition: unixfd.h:22
bool isValid() const noexcept
Check if fd is not empty.
Definition: unixfd.cpp:39
Definition: action.cpp:17
Class to handler dynamic library.
Utility class to handle unix file decriptor.
New Utility classes to handle application specific path.
int fd() const noexcept
Get the internal fd.
Definition: unixfd.cpp:41
const char * backwardSearch(const char *haystack, size_t l, const char *needle, size_t ol, size_t from)
Search string needle of size ol in string haystack.
static UnixFD openPath(const std::filesystem::path &path, std::optional< int > flags=std::nullopt, std::optional< mode_t > mode=std::nullopt)
Open the path.
String handle utilities.
Helper template class to make easier to use type safe enum flags.