forked from AppImageCommunity/AppImageUpdate
-
Notifications
You must be signed in to change notification settings - Fork 0
/
util.h
295 lines (232 loc) · 9.69 KB
/
util.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
#pragma once
// system headers
#include <algorithm>
#include <climits>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
#include <unistd.h>
#ifdef FLTK_UI
#include <FL/Fl.H>
#endif
#ifdef QT_UI
#include <QMessageBox>
#endif
// library includes
#include <zsutil.h>
// AppImageKit includes
extern "C" {
#include "appimage/appimage_shared.h"
}
namespace appimage {
namespace update {
static void removeNewlineCharacters(std::string& str) {
str.erase(std::remove(str.begin(), str.end(), '\n'), str.end());
}
static inline bool ltrim(std::string& s, char to_trim = ' ') {
// TODO: find more efficient way to check whether elements have been removed
size_t initialLength = s.length();
s.erase(s.begin(), std::find_if(s.begin(), s.end(), [to_trim](int ch) {
return ch != to_trim;
}));
return s.length() < initialLength;
}
static inline bool rtrim(std::string& s, char to_trim = ' ') {
// TODO: find more efficient way to check whether elements have been removed
auto initialLength = s.length();
s.erase(std::find_if(s.rbegin(), s.rend(), [to_trim](int ch) {
return ch != to_trim;
}).base(), s.end());
return s.length() < initialLength;
}
static inline bool trim(std::string& s, char to_trim = ' ') {
// returns true if either modifies s
auto ltrim_result = ltrim(s, to_trim);
return rtrim(s, to_trim) && ltrim_result;
}
static bool callProgramAndGrepForLine(const std::string& command, const std::string& pattern,
std::string& output) {
FILE *stream = popen(command.c_str(), "r");
if (stream == nullptr)
return false;
char *line;
size_t lineSize = 0;
while(getline(&line, &lineSize, stream)) {
// check whether line matches pattern
std::string lineString = line;
if (lineString.find(pattern) != std::string::npos) {
if (pclose(stream) != 0) {
free(line);
return false;
}
output = line;
removeNewlineCharacters(output);
return true;
}
}
if (pclose(stream) != 0) {
free(line);
return false;
}
return false;
}
static std::vector<std::string> split(const std::string& s, char delim = ' ') {
std::vector<std::string> result;
std::stringstream ss(s);
std::string item;
while (std::getline(ss, item, delim)) {
result.push_back(item);
}
return result;
}
static inline std::string toLower(std::string s) {
std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) { return std::tolower(c); });
return s;
}
static inline bool toLong(const std::string& str, long& retval, int base = 10) {
char* end = nullptr;
const auto* cstr = str.c_str();
auto rv = std::strtol(cstr, &end, base);
if (errno == ERANGE || cstr == end || retval > LONG_MAX || retval < LONG_MIN)
return false;
retval = rv;
return true;
}
static inline bool isFile(const std::string& path) {
std::ifstream ifs(path);
return (bool) ifs && ifs.good();
}
static void copyPermissions(const std::string& oldPath, const std::string& newPath) {
mode_t oldPerms;
auto errCode = zsync2::getPerms(oldPath, oldPerms);
if (errCode != 0) {
std::ostringstream ss;
ss << "Error calling stat(): " << strerror(errCode);
#ifdef FLTK_UI
fl_message("%s", ss.str().c_str());
#endif
#ifdef QT_UI
QMessageBox::critical(nullptr, "Error", QString::fromStdString(ss.str()), QMessageBox::Close);
#endif
exit(1);
}
chmod(newPath.c_str(), oldPerms);
}
static void runApp(const std::string& path) {
// make executable
mode_t newPerms;
auto errCode = zsync2::getPerms(path, newPerms);
if (errCode != 0) {
std::ostringstream ss;
ss << "Error calling stat(): " << strerror(errCode);
#ifdef FLTK_UI
fl_message("%s", ss.str().c_str());
#endif
#ifdef QT_UI
QMessageBox::critical(nullptr, "Error", QString::fromStdString(ss.str()), QMessageBox::Close);
#endif
exit(1);
}
chmod(path.c_str(), newPerms | S_IXUSR);
// full path to AppImage, required for execl
char* realPathToAppImage;
if ((realPathToAppImage = realpath(path.c_str(), nullptr)) == nullptr) {
auto error = errno;
std::ostringstream ss;
ss << "Error resolving full path of AppImage: code " << error << ": " << strerror(error) << std::endl;
#ifdef FLTK_UI
fl_message("%s", ss.str().c_str());
#endif
#ifdef QT_UI
QMessageBox::critical(nullptr, "Error", QString::fromStdString(ss.str()), QMessageBox::Close);
#endif
exit(1);
}
if (fork() == 0) {
putenv(strdup("STARTED_BY_APPIMAGEUPDATE=1"));
std::cerr << "Running " << realPathToAppImage << std::endl;
// make sure to deactivate updater contained in the AppImage when running from AppImageUpdate
execl(realPathToAppImage, realPathToAppImage, nullptr);
// execle should never return, so if this code is reached, there must be an error
auto error = errno;
std::cerr << "Error executing AppImage " << realPathToAppImage << ": code " << error << ": "
<< strerror(error) << std::endl;
exit(1);
}
}
// Reads an ELF file section and returns its contents.
static std::string readElfSection(const std::string& filePath, const std::string& sectionName) {
unsigned long offset = 0, length = 0;
auto rv = appimage_get_elf_section_offset_and_length(filePath.c_str(), sectionName.c_str(), &offset, &length);
if (!rv || offset == 0 || length == 0)
return "";
std::ifstream ifs(filePath);
ifs.seekg(offset);
std::vector<char> buffer(length+1, 0);
ifs.read(buffer.data(), length);
return buffer.data();
}
static std::string findInPATH(const std::string& name) {
const std::string PATH = getenv("PATH");
for (const auto& path : split(PATH, ':')) {
std::ostringstream oss;
oss << path << "/" << name;
auto fullPath = oss.str();
// TODO: check whether file is actually executable
if (isFile(fullPath))
return fullPath;
}
return "";
}
static bool stringStartsWith(const std::string& string, const std::string& prefix) {
return strncmp(string.c_str(), prefix.c_str(), prefix.size()) == 0;
}
static std::string abspath(const std::string& path) {
char* fullPath = nullptr;
if ((fullPath = realpath(path.c_str(), nullptr)) == nullptr) {
auto error = errno;
std::cerr << "Failed to resolve full path to AppImage: " << strerror(error) << std::endl;
return "";
}
std::string rv = fullPath;
// clean up
free(fullPath);
fullPath = nullptr;
return rv;
}
static std::string pathToOldAppImage(const std::string& oldPath, const std::string& newPath) {
if (oldPath == newPath) {
return newPath + ".zs-old";
}
return abspath(oldPath);
};
// workaround for AppImageLauncher limitation, see https://github.com/AppImage/AppImageUpdate/issues/131
static std::string ailfsRealpath(const std::string& path) {
std::stringstream ailfsBasePath;
ailfsBasePath << "/run/user/" << getuid() << "/appimagelauncherfs/";
if (path.find(ailfsBasePath.str()) == std::string::npos)
return path;
std::stringstream mapFilePath;
mapFilePath << ailfsBasePath.str() << "/map";
std::ifstream ifs(mapFilePath.str());
if (!ifs)
throw std::runtime_error("Could not open appimagelauncherfs map file");
std::string pathFileName;
{
std::unique_ptr<char> pathCStr(strdup(path.c_str()));
pathFileName = basename(pathCStr.get());
}
std::string currentLine;
while (std::getline(ifs, currentLine)) {
const std::string delim = " -> ";
const auto delimiterPos = currentLine.find(delim);
const auto ailfsFileName = currentLine.substr(0, delimiterPos);
const auto targetFilePath = currentLine.substr(delimiterPos + delim.length());
if (ailfsFileName == pathFileName)
return targetFilePath;
}
throw std::runtime_error("Could not resolve path in appimagelauncherfs map file");
}
}; // namespace update
} // namespace appimage