]>
Commit | Line | Data |
---|---|---|
221cc3ce MT |
1 | /*############################################################################# |
2 | # # | |
3 | # Pakfire - The IPFire package management system # | |
4 | # Copyright (C) 2013 Pakfire development team # | |
5 | # # | |
6 | # This program is free software: you can redistribute it and/or modify # | |
7 | # it under the terms of the GNU General Public License as published by # | |
8 | # the Free Software Foundation, either version 3 of the License, or # | |
9 | # (at your option) any later version. # | |
10 | # # | |
11 | # This program is distributed in the hope that it will be useful, # | |
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of # | |
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # | |
14 | # GNU General Public License for more details. # | |
15 | # # | |
16 | # You should have received a copy of the GNU General Public License # | |
17 | # along with this program. If not, see <http://www.gnu.org/licenses/>. # | |
18 | # # | |
19 | #############################################################################*/ | |
20 | ||
5837952f | 21 | #include <ctype.h> |
3854acbd | 22 | #include <errno.h> |
62b60e37 | 23 | #include <fcntl.h> |
e75d1e3c | 24 | #include <ftw.h> |
221cc3ce MT |
25 | #include <libgen.h> |
26 | #include <math.h> | |
27 | #include <stddef.h> | |
28 | #include <stdio.h> | |
29 | #include <stdlib.h> | |
30 | #include <string.h> | |
6582a143 | 31 | #include <sys/resource.h> |
3854acbd MT |
32 | #include <sys/stat.h> |
33 | #include <sys/types.h> | |
526e4cec | 34 | #include <time.h> |
3854acbd | 35 | #include <unistd.h> |
221cc3ce | 36 | |
88124868 | 37 | #include <archive.h> |
53f5745e | 38 | #include <archive_entry.h> |
0351af83 | 39 | #include <json.h> |
9bddb328 | 40 | #include <solv/pool.h> |
df552c32 MT |
41 | #include <uuid/uuid.h> |
42 | ||
221cc3ce | 43 | #include <pakfire/constants.h> |
3854acbd | 44 | #include <pakfire/logging.h> |
31480bee | 45 | #include <pakfire/package.h> |
9bddb328 | 46 | #include <pakfire/util.h> |
221cc3ce | 47 | |
563e9600 MT |
48 | #define NSEC_PER_SEC 1000000000 |
49 | ||
9bddb328 MT |
50 | static Id pakfire_parse_namespace(Pool* pool, const char* s) { |
51 | const char* p = strchr(s, '('); | |
52 | if (!p) | |
53 | return 0; | |
54 | ||
55 | // Store the namespace ID | |
56 | Id namespace = pool_strn2id(pool, s, p - s, 1); | |
57 | ||
58 | // Find the end of the string | |
59 | s = strrchr(p, ')'); | |
60 | if (!s) | |
61 | return 0; | |
62 | ||
63 | Id id = pool_strn2id(pool, p + 1, s - p - 1, 1); | |
64 | ||
65 | // Bring it all together | |
66 | return pool_rel2id(pool, namespace, id, REL_NAMESPACE, 1); | |
67 | } | |
68 | ||
ac4c607b | 69 | Id pakfire_parse_dep(struct pakfire* pakfire, const char* s) { |
9bddb328 MT |
70 | Id id; |
71 | ||
72 | if (!s) { | |
73 | errno = EINVAL; | |
74 | return 0; | |
75 | } | |
76 | ||
075bebef MT |
77 | // Ignore empty strings |
78 | if (!*s) | |
79 | return 0; | |
80 | ||
9bddb328 MT |
81 | Pool* pool = pakfire_get_solv_pool(pakfire); |
82 | ||
83 | // Consume any leading space | |
84 | if (isspace(*s)) | |
85 | s++; | |
86 | ||
87 | const char* p = s; | |
88 | ||
89 | // Find the first part (before =, >= or <=) | |
90 | while (*p && !isspace(*p) && *p != '<' && *p != '=' && *p != '>') | |
91 | p++; | |
92 | ||
93 | // The length of the first part | |
94 | size_t l = p - s; | |
95 | ||
96 | // Add name to pool | |
97 | if (pakfire_string_startswith(s, "pakfire(")) | |
98 | id = pakfire_parse_namespace(pool, s); | |
99 | else | |
100 | id = pool_strn2id(pool, s, l, 1); | |
101 | ||
102 | // Consume any more space | |
103 | if (isspace(*p)) | |
104 | p++; | |
105 | ||
106 | if (*p == '<' || *p == '=' || *p == '>') { | |
107 | int flags = 0; | |
108 | ||
109 | while (1) { | |
110 | if (*p == '<') | |
111 | flags |= REL_LT; | |
112 | else if (*p == '=') | |
113 | flags |= REL_EQ; | |
114 | else if (*p == '>') | |
115 | flags |= REL_GT; | |
116 | else | |
117 | break; | |
118 | ||
119 | p++; | |
120 | } | |
121 | ||
122 | // Consume any more space | |
123 | if (isspace(*p)) | |
124 | p++; | |
125 | ||
126 | // Add EVR to pool | |
127 | Id evr = pool_str2id(pool, p, 1); | |
128 | ||
129 | // Combine everything | |
130 | id = pool_rel2id(pool, id, evr, flags, 1); | |
131 | } | |
132 | ||
133 | return id; | |
134 | } | |
135 | ||
ac4c607b | 136 | void pakfire_parse_deps(struct pakfire* pakfire, struct pakfire_package* pkg, |
31480bee | 137 | void (*func)(struct pakfire_package* pkg, const char* dep), const char* deps) { |
b81a64ec MT |
138 | char* p = strdupa(deps); |
139 | ||
140 | while (*p) { | |
141 | char* e = strchr(p, '\n'); | |
142 | ||
143 | // Terminate the string | |
144 | if (e) | |
145 | *e = '\0'; | |
146 | ||
147 | // Add the dependency | |
148 | func(pkg, p); | |
149 | ||
150 | // End loop when we reached the end | |
151 | if (!e) | |
152 | break; | |
153 | ||
154 | // Or continue at the next line | |
155 | p = e + 1; | |
156 | } | |
157 | } | |
158 | ||
714c91a2 | 159 | int pakfire_string_startswith(const char* s, const char* prefix) { |
d967ba61 MT |
160 | // Validate input |
161 | if (!s || !prefix) { | |
162 | errno = EINVAL; | |
163 | return 1; | |
164 | } | |
165 | ||
5e3ca618 | 166 | return !strncmp(s, prefix, strlen(prefix)); |
5c409596 MT |
167 | } |
168 | ||
be11aa6e | 169 | int pakfire_string_endswith(const char* s, const char* suffix) { |
9266e977 MT |
170 | // Validate input |
171 | if (!s || !suffix) { | |
172 | errno = EINVAL; | |
173 | return 1; | |
174 | } | |
175 | ||
be11aa6e MT |
176 | return !strcmp(s + strlen(s) - strlen(suffix), suffix); |
177 | } | |
178 | ||
f4d93071 MT |
179 | int pakfire_string_matches(const char* s, const char* pattern) { |
180 | return !!strstr(s, pattern); | |
181 | } | |
182 | ||
5c76be2c MT |
183 | char* pakfire_unquote_in_place(char* s) { |
184 | if (!s || !*s) | |
185 | return s; | |
186 | ||
187 | // Is the first character a quote? | |
188 | if (*s != '"') | |
189 | return s; | |
190 | ||
191 | // Find the end of value | |
192 | size_t l = strlen(s); | |
193 | if (!l) | |
194 | return s; | |
195 | ||
196 | // Is the last character a quote? | |
197 | if (s[l - 1] != '"') | |
198 | return s; | |
199 | ||
200 | // The string seems to be in quotes; remove them | |
201 | s[l - 1] = '\0'; | |
202 | s++; | |
203 | ||
204 | return s; | |
205 | } | |
206 | ||
89dbb9ac | 207 | int pakfire_string_partition(const char* s, const char* delim, char** s1, char** s2) { |
5837952f MT |
208 | char* p = strstr(s, delim); |
209 | ||
210 | // Delim was not found | |
211 | if (!p) { | |
212 | *s1 = NULL; | |
213 | *s2 = NULL; | |
214 | return 1; | |
215 | } | |
216 | ||
217 | // Length of string before delim | |
218 | size_t l = p - s; | |
219 | ||
220 | char* buffer = malloc(l + 1); | |
221 | if (!buffer) | |
222 | return -ENOMEM; | |
223 | ||
224 | // Copy first part | |
225 | *s1 = memcpy(buffer, s, l); | |
226 | buffer[l] = '\0'; | |
227 | ||
228 | // Copy second part | |
229 | *s2 = strdup(p + strlen(delim)); | |
230 | ||
231 | return 0; | |
232 | } | |
233 | ||
8de06755 | 234 | char* pakfire_string_replace(const char* s, const char* pattern, const char* repl) { |
b961e8d5 MT |
235 | // Return NULL on no input or no pattern |
236 | if (!s || !pattern) { | |
237 | errno = EINVAL; | |
238 | return NULL; | |
239 | } | |
240 | ||
241 | // Replace with nothing when repl is NULL | |
242 | if (!repl) | |
243 | repl = ""; | |
244 | ||
b8377a9f MT |
245 | char* result = NULL; |
246 | const char** cache = NULL; | |
247 | unsigned int count = 0; | |
248 | ||
249 | const size_t pattern_length = strlen(pattern); | |
250 | ||
251 | // Working pointer | |
252 | const char* p = s; | |
253 | ||
254 | // Find all occurrences of pattern and store their location | |
255 | while (1) { | |
256 | const char* needle = strstr(p, pattern); | |
257 | if (!needle) | |
258 | break; | |
259 | ||
260 | // Make space in the cache | |
261 | cache = reallocarray(cache, sizeof(*cache), count + 1); | |
262 | cache[count++] = needle; | |
263 | ||
264 | // Move p forward | |
265 | p = needle + pattern_length; | |
266 | } | |
267 | ||
268 | // Copy the string if no occurence was found | |
269 | if (count == 0) { | |
270 | result = strdup(s); | |
271 | goto ERROR; | |
272 | } | |
273 | ||
274 | // Store the end pointer | |
275 | cache = reallocarray(cache, sizeof(*cache), count + 1); | |
276 | cache[count] = s + strlen(s); | |
277 | ||
278 | const size_t repl_length = strlen(repl); | |
279 | ||
280 | // Determine the length of the final string | |
281 | const size_t length = strlen(s) + ((repl_length - pattern_length) * count); | |
282 | ||
283 | // Allocate enough memory for the result | |
284 | result = malloc(length + 1); | |
285 | if (!result) | |
286 | goto ERROR; | |
287 | ||
288 | // Reset p | |
289 | p = s; | |
290 | ||
291 | // Working pointer for the result | |
292 | char* r = result; | |
293 | ||
294 | // Copy everything up to the first match | |
295 | ssize_t l = cache[0] - s; | |
296 | memcpy(r, p, l); | |
297 | r += l; | |
298 | p += l; | |
299 | ||
300 | for (unsigned int i = 0; i < count; i++) { | |
301 | // Put replacement here | |
302 | memcpy(r, repl, repl_length); | |
303 | r += repl_length; | |
304 | p += pattern_length; | |
305 | ||
306 | // Determine the length between two matches | |
307 | l = cache[i+1] - (cache[i] + pattern_length); | |
308 | ||
309 | memcpy(r, p, l); | |
310 | r += l; | |
311 | p += l; | |
312 | } | |
313 | ||
314 | // Terminate the string | |
315 | result[length] = '\0'; | |
316 | ||
317 | ERROR: | |
318 | if (cache) | |
319 | free(cache); | |
320 | ||
321 | return result; | |
322 | } | |
323 | ||
c81287d2 MT |
324 | static unsigned int pakfire_chrcnt(const char* s, char delim) { |
325 | size_t length = strlen(s); | |
603ca1e0 | 326 | |
c81287d2 | 327 | unsigned int count = 0; |
603ca1e0 | 328 | |
c81287d2 MT |
329 | for (unsigned int i = 0; i < length; i++) |
330 | if (s[i] == delim) | |
603ca1e0 | 331 | count++; |
c81287d2 MT |
332 | |
333 | return count; | |
334 | } | |
335 | ||
336 | char** pakfire_split_string(const char* s, char delim) { | |
337 | char** array = NULL; | |
338 | ||
339 | if (!s) { | |
340 | errno = EINVAL; | |
341 | return NULL; | |
603ca1e0 MT |
342 | } |
343 | ||
c81287d2 MT |
344 | // Count how often we need to split |
345 | unsigned int count = pakfire_chrcnt(s, delim) + 1; | |
346 | ||
347 | // Allocate array | |
348 | array = calloc(count + 1, sizeof(*array)); | |
349 | if (!array) | |
350 | return NULL; | |
351 | ||
352 | // Copy string to stack | |
353 | char* p = strdupa(s); | |
354 | if (!p) | |
355 | return NULL; | |
603ca1e0 | 356 | |
603ca1e0 | 357 | unsigned int i = 0; |
603ca1e0 | 358 | while (*p) { |
c81287d2 | 359 | char* e = strchr(p, delim); |
603ca1e0 | 360 | |
c81287d2 MT |
361 | // Terminate the string |
362 | if (e) | |
363 | *e = '\0'; | |
364 | ||
365 | // Add string to the array | |
366 | array[i++] = strdup(p); | |
603ca1e0 | 367 | |
c81287d2 MT |
368 | // End loop when we reached the end |
369 | if (!e) | |
370 | break; | |
371 | ||
372 | // Or continue at the next line | |
373 | p = e + 1; | |
374 | } | |
603ca1e0 | 375 | |
c81287d2 | 376 | return array; |
603ca1e0 MT |
377 | } |
378 | ||
b03e1127 MT |
379 | char* pakfire_string_join(char** list, const char* delim) { |
380 | if (!list) | |
381 | return NULL; | |
382 | ||
383 | size_t length = 0; | |
384 | unsigned int elements = 0; | |
385 | ||
386 | // Count the number of elements and the total length | |
387 | for (char** item = list; *item; item++) { | |
388 | length += strlen(*item); | |
389 | elements++; | |
390 | } | |
391 | ||
392 | // Empty list? | |
393 | if (!elements) | |
394 | return NULL; | |
395 | ||
396 | // Add the delimiters | |
397 | length += strlen(delim) * (elements - 1); | |
398 | ||
399 | // Allocate the result string | |
400 | char* string = malloc(length + 1); | |
401 | if (!string) | |
402 | return NULL; | |
403 | ||
404 | // Pointer to where we are writing | |
405 | char* p = string; | |
406 | ||
407 | size_t bytes_left = length; | |
408 | size_t bytes_written; | |
409 | ||
410 | for (char** item = list; *item; item++) { | |
411 | bytes_written = snprintf(p, bytes_left, "%s", *item); | |
412 | ||
413 | bytes_left -= bytes_written; | |
414 | p += bytes_written; | |
415 | ||
416 | // Write the delimiter | |
417 | if (bytes_left) { | |
418 | bytes_written = snprintf(p, bytes_left, "%s", delim); | |
419 | ||
420 | bytes_left -= bytes_written; | |
421 | p += bytes_written; | |
422 | } | |
423 | } | |
424 | ||
425 | return string; | |
426 | } | |
427 | ||
cc90545a | 428 | int pakfire_format_size(char* dst, size_t length, double value) { |
df56e4ef | 429 | const char* units[] = { |
3f1f5408 MT |
430 | "%.0f ", |
431 | "%.0fk", | |
432 | "%.1fM", | |
433 | "%.1fG", | |
434 | "%.1fT", | |
df56e4ef MT |
435 | NULL |
436 | }; | |
221cc3ce MT |
437 | const char** unit = units; |
438 | ||
cc90545a MT |
439 | while (*(unit + 1) && value >= 1024.0) { |
440 | value /= 1024.0; | |
221cc3ce MT |
441 | unit++; |
442 | } | |
443 | ||
df56e4ef MT |
444 | #pragma GCC diagnostic push |
445 | #pragma GCC diagnostic ignored "-Wformat-nonliteral" | |
68d26a6a MT |
446 | return snprintf(dst, length, *unit, value); |
447 | #pragma GCC diagnostic pop | |
448 | } | |
449 | ||
450 | int pakfire_format_speed(char* dst, size_t length, double value) { | |
451 | const char* units[] = { | |
452 | "%4.0fB/s", | |
453 | "%4.0fkB/s", | |
454 | "%4.1fMB/s", | |
455 | "%4.1fGB/s", | |
456 | "%4.1fTB/s", | |
457 | NULL | |
458 | }; | |
459 | const char** unit = units; | |
460 | ||
461 | while (*(unit + 1) && value >= 1024.0) { | |
462 | value /= 1024.0; | |
463 | unit++; | |
464 | } | |
465 | ||
466 | #pragma GCC diagnostic push | |
467 | #pragma GCC diagnostic ignored "-Wformat-nonliteral" | |
468 | return snprintf(dst, length, *unit, value); | |
df56e4ef | 469 | #pragma GCC diagnostic pop |
221cc3ce MT |
470 | } |
471 | ||
526e4cec MT |
472 | #pragma GCC diagnostic push |
473 | #pragma GCC diagnostic ignored "-Wformat-nonliteral" | |
474 | static char* pakfire_strftime(const char* format, time_t t) { | |
7500066c | 475 | char string[128]; |
526e4cec MT |
476 | struct tm* tm = gmtime(&t); |
477 | ||
7500066c | 478 | strftime(string, sizeof(string) - 1, format, tm); |
526e4cec | 479 | |
5d901566 | 480 | return strdup(string); |
526e4cec MT |
481 | } |
482 | #pragma GCC diagnostic pop | |
483 | ||
484 | char* pakfire_format_date(time_t t) { | |
485 | return pakfire_strftime("%Y-%m-%d", t); | |
486 | } | |
487 | ||
5f2ed202 MT |
488 | int __pakfire_strftime_now(char* dest, size_t length, const char* format) { |
489 | struct tm tm; | |
490 | ||
491 | // Fetch the current time | |
492 | const time_t t = time(NULL); | |
493 | if (t < 0) | |
494 | return 1; | |
495 | ||
496 | // Convert to struct tm | |
497 | struct tm* now = gmtime_r(&t, &tm); | |
498 | if (!now) | |
499 | return 1; | |
500 | ||
501 | #pragma GCC diagnostic push | |
502 | #pragma GCC diagnostic ignored "-Wformat-nonliteral" | |
503 | strftime(dest, length, format, now); | |
504 | #pragma GCC diagnostic pop | |
505 | ||
506 | return 0; | |
507 | } | |
508 | ||
99865093 | 509 | int __pakfire_path_join(char* dest, size_t length, |
384d009a | 510 | const char* first, const char* second) { |
14143513 MT |
511 | if (!first) |
512 | return snprintf(dest, length, "%s", second); | |
513 | ||
221cc3ce | 514 | if (!second) |
384d009a | 515 | return snprintf(dest, length, "%s", first); |
221cc3ce | 516 | |
14143513 MT |
517 | // Remove leading slashes from second argument |
518 | while (*second == '/') | |
519 | second++; | |
221cc3ce | 520 | |
384d009a | 521 | return snprintf(dest, length, "%s/%s", first, second); |
221cc3ce MT |
522 | } |
523 | ||
36790d86 | 524 | const char* pakfire_path_relpath(const char* root, const char* path) { |
5e3ca618 | 525 | if (pakfire_string_startswith(path, root)) |
55b57cf6 | 526 | return path + strlen(root); |
55b57cf6 MT |
527 | |
528 | return NULL; | |
529 | } | |
530 | ||
8c22118b MT |
531 | int pakfire_path_exists(const char* path) { |
532 | return !access(path, F_OK); | |
533 | } | |
534 | ||
a5e9cd4b MT |
535 | time_t pakfire_path_age(const char* path) { |
536 | struct stat st; | |
537 | ||
538 | int r = stat(path, &st); | |
539 | if (r == 0) { | |
540 | // Get current timestamp | |
541 | time_t now = time(NULL); | |
542 | ||
543 | // Return the difference since the file has been created and now | |
544 | return now - st.st_ctime; | |
545 | } | |
546 | ||
547 | return -1; | |
548 | } | |
549 | ||
05e8d3c9 | 550 | char* pakfire_basename(const char* path) { |
5d901566 | 551 | char* name = strdup(path); |
221cc3ce | 552 | |
2e9ddb9c | 553 | char* r = basename(name); |
b196ec9d | 554 | if (r) |
5d901566 | 555 | r = strdup(r); |
b196ec9d | 556 | |
f0d6233d | 557 | free(name); |
d11561f7 MT |
558 | |
559 | return r; | |
221cc3ce MT |
560 | } |
561 | ||
41a7a52f | 562 | char* pakfire_dirname(const char* path) { |
5d901566 | 563 | char* parent = strdup(path); |
221cc3ce | 564 | |
2e9ddb9c | 565 | char* r = dirname(parent); |
b196ec9d | 566 | if (r) |
5d901566 | 567 | r = strdup(r); |
b196ec9d | 568 | |
f0d6233d | 569 | free(parent); |
d11561f7 MT |
570 | |
571 | return r; | |
221cc3ce MT |
572 | } |
573 | ||
221cc3ce MT |
574 | char* pakfire_remove_trailing_newline(char* str) { |
575 | ssize_t pos = strlen(str) - 1; | |
576 | ||
577 | if (str[pos] == '\n') | |
578 | str[pos] = '\0'; | |
579 | ||
580 | return str; | |
581 | } | |
658c740d | 582 | |
a566ff7f | 583 | int pakfire_read_file_into_buffer(FILE* f, char** buffer, size_t* len) { |
3bf7e142 MT |
584 | if (!f) |
585 | return -EBADF; | |
586 | ||
587 | int r = fseek(f, 0, SEEK_END); | |
588 | if (r) | |
589 | return r; | |
590 | ||
591 | // Save length of file | |
592 | *len = ftell(f); | |
593 | ||
594 | // Go back to the start | |
595 | r = fseek(f, 0, SEEK_SET); | |
596 | if (r) | |
597 | return r; | |
598 | ||
599 | // Allocate buffer | |
547759ae | 600 | *buffer = malloc((sizeof(**buffer) * *len) + 1); |
3bf7e142 MT |
601 | if (!*buffer) |
602 | return -ENOMEM; | |
603 | ||
604 | // Read content | |
605 | fread(*buffer, *len, sizeof(**buffer), f); | |
606 | ||
607 | // Check we encountered any errors | |
608 | r = ferror(f); | |
609 | if (r) { | |
f0d6233d | 610 | free(*buffer); |
3bf7e142 MT |
611 | return r; |
612 | } | |
613 | ||
614 | // Terminate the buffer | |
615 | (*buffer)[*len] = '\0'; | |
616 | ||
617 | return 0; | |
618 | } | |
312fd26f | 619 | |
ab96da22 | 620 | char* pakfire_generate_uuid() { |
df552c32 MT |
621 | uuid_t uuid; |
622 | ||
623 | // Generate a new random value | |
624 | uuid_generate_random(uuid); | |
625 | ||
626 | char* ret = malloc(UUID_STR_LEN + 1); | |
627 | if (!ret) | |
628 | return NULL; | |
629 | ||
630 | // Convert it to string | |
631 | uuid_unparse_lower(uuid, ret); | |
632 | ||
633 | // Terminate string | |
634 | ret[UUID_STR_LEN] = '\0'; | |
635 | ||
636 | return ret; | |
637 | } | |
7ab0465c | 638 | |
457feacb MT |
639 | int pakfire_tty_is_noninteractive(void) { |
640 | const int fds[] = { | |
641 | STDIN_FILENO, | |
642 | STDOUT_FILENO, | |
643 | STDERR_FILENO, | |
644 | -1, | |
645 | }; | |
646 | ||
647 | for (const int* fd = fds; *fd >= 0; fd++) { | |
648 | if (!isatty(*fd)) | |
649 | return 1; | |
650 | } | |
651 | ||
652 | return 0; | |
653 | } | |
654 | ||
fa106e1b | 655 | char* __pakfire_hexlify(const unsigned char* digest, const size_t length) { |
7ab0465c MT |
656 | const char* hexdigits = "0123456789abcdef"; |
657 | ||
658 | char* s = malloc((length * 2) + 1); | |
659 | if (!s) | |
660 | return NULL; | |
661 | ||
662 | for (unsigned int i = 0, j = 0; i < length; i++) { | |
663 | char b = digest[i]; | |
664 | ||
665 | s[j++] = hexdigits[(b >> 4) & 0xf]; | |
666 | s[j++] = hexdigits[(b & 0x0f)]; | |
667 | } | |
668 | ||
669 | // Terminate result | |
670 | s[length * 2] = '\0'; | |
671 | ||
672 | return s; | |
673 | } | |
62b60e37 | 674 | |
d0e6b214 MT |
675 | static int parse_nibble(char nibble) { |
676 | // Handle digits | |
677 | if (nibble >= '0' && nibble <= '9') | |
678 | return nibble - '0'; | |
679 | ||
680 | // Handle lowercase letters | |
681 | if (nibble >= 'a' && nibble <= 'f') | |
682 | return 10 + nibble - 'a'; | |
683 | ||
684 | // Handle uppercase letters | |
685 | if (nibble >= 'A' && nibble <= 'F') | |
686 | return 10 + nibble - 'A'; | |
687 | ||
688 | // Error | |
689 | return -1; | |
690 | } | |
691 | ||
01f8f6b0 | 692 | int __pakfire_unhexlify(unsigned char* dst, const size_t l, const char* src) { |
d0e6b214 MT |
693 | if (!src) { |
694 | errno = EINVAL; | |
695 | return 1; | |
696 | } | |
8bfcf741 | 697 | |
d0e6b214 MT |
698 | // Determine input length |
699 | const size_t length = strlen(src); | |
700 | ||
701 | for (unsigned int i = 0, j = 0; i < length && j < l; i += 2, j++) { | |
702 | int h = parse_nibble(src[i]); | |
703 | int l = parse_nibble(src[i+1]); | |
704 | ||
705 | // Check for invalid input | |
706 | if (h < 0 || l < 0) { | |
707 | errno = EINVAL; | |
8bfcf741 | 708 | return 1; |
d0e6b214 | 709 | } |
8bfcf741 | 710 | |
d0e6b214 MT |
711 | // Store result |
712 | dst[j] = h << 4 | l; | |
8bfcf741 MT |
713 | } |
714 | ||
715 | return 0; | |
716 | } | |
717 | ||
a29c7190 | 718 | int pakfire_mkparentdir(const char* path, mode_t mode) { |
62b60e37 MT |
719 | int r; |
720 | ||
721 | char* dirname = pakfire_dirname(path); | |
722 | if (!dirname) | |
723 | return 1; | |
724 | ||
725 | // We have arrived at the top of the tree | |
aba60128 | 726 | if (*dirname == '.' || strcmp(dirname, "/") == 0) |
62b60e37 MT |
727 | return 0; |
728 | ||
729 | // Ensure the parent directory exists | |
4d47c163 | 730 | r = pakfire_mkparentdir(dirname, mode); |
aba60128 | 731 | if (r) |
62b60e37 | 732 | goto END; |
62b60e37 MT |
733 | |
734 | // Create this directory | |
4d47c163 | 735 | r = mkdir(dirname, mode); |
62b60e37 | 736 | |
aba60128 MT |
737 | // Ignore when the directory already exists |
738 | if (r && errno == EEXIST) | |
739 | r = 0; | |
740 | ||
62b60e37 MT |
741 | END: |
742 | free(dirname); | |
743 | ||
744 | return r; | |
745 | } | |
746 | ||
4d47c163 MT |
747 | int pakfire_mkdir(const char* path, mode_t mode) { |
748 | int r = pakfire_mkparentdir(path, mode); | |
749 | if (r) | |
750 | return r; | |
751 | ||
752 | // Finally, create the directory we want | |
753 | return mkdir(path, mode); | |
754 | } | |
755 | ||
62b60e37 | 756 | FILE* pakfire_mktemp(char* path) { |
4d47c163 | 757 | int r = pakfire_mkparentdir(path, 0); |
aba60128 MT |
758 | if (r) |
759 | return NULL; | |
62b60e37 MT |
760 | |
761 | // Create a temporary result file | |
762 | int fd = mkostemp(path, O_CLOEXEC); | |
763 | if (fd < 0) | |
764 | return NULL; | |
765 | ||
766 | // Re-open as file handle | |
767 | return fdopen(fd, "w+"); | |
768 | } | |
0351af83 | 769 | |
e75d1e3c | 770 | char* pakfire_mkdtemp(char* path) { |
4d47c163 | 771 | int r = pakfire_mkparentdir(path, 0); |
e75d1e3c MT |
772 | if (r) |
773 | return NULL; | |
774 | ||
775 | return mkdtemp(path); | |
776 | } | |
777 | ||
778 | static int _unlink(const char* path, const struct stat* stat, int typeflag, struct FTW* ftwbuf) { | |
779 | return remove(path); | |
780 | } | |
781 | ||
782 | int pakfire_rmtree(const char* path, int flags) { | |
783 | int r = nftw(path, _unlink, 64, flags|FTW_DEPTH|FTW_PHYS); | |
784 | ||
785 | // Ignore if path didn't exist | |
786 | if (r < 0 && errno == ENOENT) | |
787 | r = 0; | |
788 | ||
789 | return r; | |
790 | } | |
791 | ||
f5aee1e4 MT |
792 | // Digests |
793 | ||
794 | size_t pakfire_digest_length(enum pakfire_digests digest) { | |
795 | switch (digest) { | |
796 | case PAKFIRE_DIGEST_SHA512: | |
797 | return 64; | |
798 | ||
799 | case PAKFIRE_DIGEST_SHA256: | |
800 | return 32; | |
801 | ||
5e8dfbeb MT |
802 | case PAKFIRE_DIGEST_SHA1: |
803 | return 20; | |
804 | ||
f5aee1e4 MT |
805 | case PAKFIRE_DIGEST_NONE: |
806 | return 0; | |
807 | } | |
808 | ||
809 | return 0; | |
810 | } | |
811 | ||
88124868 MT |
812 | // Archive Stuff |
813 | ||
814 | int pakfire_archive_copy_data(struct archive* src, struct archive* dst, | |
815 | struct archive_entry* entry) { | |
816 | const void* buffer; | |
817 | size_t size; | |
818 | off_t offset; | |
819 | int r; | |
820 | ||
821 | for (;;) { | |
822 | // Read a block of data | |
823 | r = archive_read_data_block(src, &buffer, &size, &offset); | |
824 | if (r == ARCHIVE_EOF) | |
825 | return ARCHIVE_OK; | |
826 | else if (r) | |
827 | return r; | |
828 | ||
829 | // Write the read block of data | |
830 | r = archive_write_data(dst, buffer, size); | |
831 | if (r < 0) | |
832 | return r; | |
833 | } | |
834 | } | |
835 | ||
ac4c607b | 836 | int pakfire_archive_copy_data_to_buffer(struct pakfire* pakfire, struct archive* a, |
53f5745e MT |
837 | struct archive_entry* entry, char** data, size_t* data_size) { |
838 | *data = NULL; | |
839 | *data_size = 0; | |
840 | ||
841 | size_t required_size = archive_entry_size(entry); | |
842 | if (!required_size) | |
843 | return 0; | |
844 | ||
845 | // Allocate a block of the required size | |
b38802ab | 846 | *data = calloc(1, required_size + 1); |
53f5745e MT |
847 | if (!*data) |
848 | return ENOMEM; | |
849 | ||
850 | ssize_t bytes_read = archive_read_data(a, *data, required_size); | |
851 | if (bytes_read < 0) { | |
852 | ERROR(pakfire, "Could not read from archive: %s\n", archive_error_string(a)); | |
853 | free(*data); | |
854 | return 1; | |
855 | } | |
856 | ||
857 | *data_size = bytes_read; | |
858 | ||
859 | return 0; | |
860 | } | |
861 | ||
0351af83 MT |
862 | // JSON Stuff |
863 | ||
ac4c607b | 864 | static struct json_object* pakfire_json_parse(struct pakfire* pakfire, FILE* f) { |
0351af83 MT |
865 | struct json_tokener* tokener = NULL; |
866 | struct json_object* json = NULL; | |
867 | char* buffer = NULL; | |
868 | size_t length; | |
869 | ||
870 | // Read everything into memory | |
871 | int r = pakfire_read_file_into_buffer(f, &buffer, &length); | |
872 | if (r) | |
873 | goto ERROR; | |
874 | ||
875 | // Create tokener | |
876 | tokener = json_tokener_new(); | |
877 | if (!tokener) { | |
b1772bfb | 878 | ERROR(pakfire, "Could not allocate JSON tokener: %m\n"); |
0351af83 MT |
879 | goto ERROR; |
880 | } | |
881 | ||
882 | // Parse JSON from path | |
883 | json = json_tokener_parse_ex(tokener, buffer, length); | |
884 | if (!json) { | |
885 | enum json_tokener_error error = json_tokener_get_error(tokener); | |
886 | ||
887 | ERROR(pakfire, "JSON parsing error: %s\n", json_tokener_error_desc(error)); | |
888 | goto ERROR; | |
889 | } | |
890 | ||
891 | // Log what we have parsed | |
892 | DEBUG(pakfire, "Parsed JSON:\n%s\n", | |
893 | json_object_to_json_string_ext(json, | |
894 | JSON_C_TO_STRING_SPACED | JSON_C_TO_STRING_PRETTY) | |
895 | ); | |
896 | ||
897 | ERROR: | |
898 | if (buffer) | |
899 | free(buffer); | |
900 | ||
901 | if (tokener) | |
902 | json_tokener_free(tokener); | |
903 | ||
904 | return json; | |
905 | } | |
906 | ||
ac4c607b | 907 | struct json_object* pakfire_json_parse_from_file(struct pakfire* pakfire, const char* path) { |
0351af83 MT |
908 | FILE* f = fopen(path, "r"); |
909 | if (!f) | |
910 | return NULL; | |
911 | ||
912 | struct json_object* json = pakfire_json_parse(pakfire, f); | |
913 | fclose(f); | |
914 | ||
915 | return json; | |
916 | } | |
563e9600 MT |
917 | |
918 | // Time Stuff | |
919 | ||
920 | static void timespec_normalize(struct timespec* t) { | |
921 | while (t->tv_nsec >= NSEC_PER_SEC) { | |
922 | t->tv_sec++; | |
923 | t->tv_nsec -= NSEC_PER_SEC; | |
924 | } | |
925 | ||
926 | while (t->tv_nsec <= -NSEC_PER_SEC) { | |
927 | t->tv_sec--; | |
928 | t->tv_nsec += NSEC_PER_SEC; | |
929 | } | |
930 | } | |
931 | ||
932 | struct timespec timespec_add(const struct timespec* t1, const struct timespec* t2) { | |
933 | struct timespec r = { | |
934 | .tv_sec = t1->tv_sec + t2->tv_sec, | |
935 | .tv_nsec = t2->tv_nsec + t2->tv_nsec, | |
936 | }; | |
937 | ||
938 | // Correct any negative values | |
939 | timespec_normalize(&r); | |
940 | ||
941 | return r; | |
942 | } | |
943 | ||
944 | struct timespec timespec_from_ms(int milliseconds) { | |
945 | struct timespec t = { | |
946 | .tv_sec = (milliseconds / 1000), | |
947 | .tv_nsec = (milliseconds % 1000) * 1000000, | |
948 | }; | |
949 | ||
950 | return t; | |
951 | } | |
952 | ||
953 | int timespec_lt(struct timespec* t1, struct timespec* t2) { | |
954 | return ( | |
955 | t1->tv_sec < t2->tv_sec || | |
956 | (t1->tv_sec == t2->tv_sec && t1->tv_nsec < t2->tv_nsec) | |
957 | ); | |
958 | } | |
6582a143 MT |
959 | |
960 | // Resource Limits | |
961 | ||
ac4c607b | 962 | int pakfire_rlimit_set(struct pakfire* pakfire, int limit) { |
6582a143 MT |
963 | struct rlimit rl; |
964 | ||
965 | // Sanity check | |
966 | if (limit < 3) { | |
967 | errno = EINVAL; | |
968 | return 1; | |
969 | } | |
970 | ||
971 | // Fetch current configuration | |
972 | if (getrlimit(RLIMIT_NOFILE, &rl) < 0) { | |
b1772bfb | 973 | ERROR(pakfire, "Could not read RLIMIT_NOFILE: %m\n"); |
6582a143 MT |
974 | return 1; |
975 | } | |
976 | ||
977 | // Do not attempt to set higher than maximum | |
978 | if ((long unsigned int)limit > rl.rlim_max) | |
979 | limit = rl.rlim_max; | |
980 | ||
981 | rl.rlim_cur = limit; | |
982 | ||
983 | // Set the new limit | |
984 | if (setrlimit(RLIMIT_NOFILE, &rl) < 0) { | |
b1772bfb | 985 | ERROR(pakfire, "Could not set RLIMIT_NOFILE to %lu: %m\n", rl.rlim_cur); |
6582a143 MT |
986 | return 1; |
987 | } | |
988 | ||
989 | DEBUG(pakfire, "RLIMIT_NOFILE set to %d\n", limit); | |
990 | ||
991 | return 0; | |
992 | } | |
993 | ||
994 | /* | |
995 | Resets RLIMIT_NOFILE to FD_SETSIZE (e.g. 1024) | |
996 | for compatibility with software that uses select() | |
997 | */ | |
ac4c607b | 998 | int pakfire_rlimit_reset_nofile(struct pakfire* pakfire) { |
6582a143 MT |
999 | return pakfire_rlimit_set(pakfire, FD_SETSIZE); |
1000 | } |