]>
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) { |
5e3ca618 | 160 | return !strncmp(s, prefix, strlen(prefix)); |
5c409596 MT |
161 | } |
162 | ||
be11aa6e MT |
163 | int pakfire_string_endswith(const char* s, const char* suffix) { |
164 | return !strcmp(s + strlen(s) - strlen(suffix), suffix); | |
165 | } | |
166 | ||
f4d93071 MT |
167 | int pakfire_string_matches(const char* s, const char* pattern) { |
168 | return !!strstr(s, pattern); | |
169 | } | |
170 | ||
5c76be2c MT |
171 | char* pakfire_unquote_in_place(char* s) { |
172 | if (!s || !*s) | |
173 | return s; | |
174 | ||
175 | // Is the first character a quote? | |
176 | if (*s != '"') | |
177 | return s; | |
178 | ||
179 | // Find the end of value | |
180 | size_t l = strlen(s); | |
181 | if (!l) | |
182 | return s; | |
183 | ||
184 | // Is the last character a quote? | |
185 | if (s[l - 1] != '"') | |
186 | return s; | |
187 | ||
188 | // The string seems to be in quotes; remove them | |
189 | s[l - 1] = '\0'; | |
190 | s++; | |
191 | ||
192 | return s; | |
193 | } | |
194 | ||
89dbb9ac | 195 | int pakfire_string_partition(const char* s, const char* delim, char** s1, char** s2) { |
5837952f MT |
196 | char* p = strstr(s, delim); |
197 | ||
198 | // Delim was not found | |
199 | if (!p) { | |
200 | *s1 = NULL; | |
201 | *s2 = NULL; | |
202 | return 1; | |
203 | } | |
204 | ||
205 | // Length of string before delim | |
206 | size_t l = p - s; | |
207 | ||
208 | char* buffer = malloc(l + 1); | |
209 | if (!buffer) | |
210 | return -ENOMEM; | |
211 | ||
212 | // Copy first part | |
213 | *s1 = memcpy(buffer, s, l); | |
214 | buffer[l] = '\0'; | |
215 | ||
216 | // Copy second part | |
217 | *s2 = strdup(p + strlen(delim)); | |
218 | ||
219 | return 0; | |
220 | } | |
221 | ||
8de06755 | 222 | char* pakfire_string_replace(const char* s, const char* pattern, const char* repl) { |
b961e8d5 MT |
223 | // Return NULL on no input or no pattern |
224 | if (!s || !pattern) { | |
225 | errno = EINVAL; | |
226 | return NULL; | |
227 | } | |
228 | ||
229 | // Replace with nothing when repl is NULL | |
230 | if (!repl) | |
231 | repl = ""; | |
232 | ||
b8377a9f MT |
233 | char* result = NULL; |
234 | const char** cache = NULL; | |
235 | unsigned int count = 0; | |
236 | ||
237 | const size_t pattern_length = strlen(pattern); | |
238 | ||
239 | // Working pointer | |
240 | const char* p = s; | |
241 | ||
242 | // Find all occurrences of pattern and store their location | |
243 | while (1) { | |
244 | const char* needle = strstr(p, pattern); | |
245 | if (!needle) | |
246 | break; | |
247 | ||
248 | // Make space in the cache | |
249 | cache = reallocarray(cache, sizeof(*cache), count + 1); | |
250 | cache[count++] = needle; | |
251 | ||
252 | // Move p forward | |
253 | p = needle + pattern_length; | |
254 | } | |
255 | ||
256 | // Copy the string if no occurence was found | |
257 | if (count == 0) { | |
258 | result = strdup(s); | |
259 | goto ERROR; | |
260 | } | |
261 | ||
262 | // Store the end pointer | |
263 | cache = reallocarray(cache, sizeof(*cache), count + 1); | |
264 | cache[count] = s + strlen(s); | |
265 | ||
266 | const size_t repl_length = strlen(repl); | |
267 | ||
268 | // Determine the length of the final string | |
269 | const size_t length = strlen(s) + ((repl_length - pattern_length) * count); | |
270 | ||
271 | // Allocate enough memory for the result | |
272 | result = malloc(length + 1); | |
273 | if (!result) | |
274 | goto ERROR; | |
275 | ||
276 | // Reset p | |
277 | p = s; | |
278 | ||
279 | // Working pointer for the result | |
280 | char* r = result; | |
281 | ||
282 | // Copy everything up to the first match | |
283 | ssize_t l = cache[0] - s; | |
284 | memcpy(r, p, l); | |
285 | r += l; | |
286 | p += l; | |
287 | ||
288 | for (unsigned int i = 0; i < count; i++) { | |
289 | // Put replacement here | |
290 | memcpy(r, repl, repl_length); | |
291 | r += repl_length; | |
292 | p += pattern_length; | |
293 | ||
294 | // Determine the length between two matches | |
295 | l = cache[i+1] - (cache[i] + pattern_length); | |
296 | ||
297 | memcpy(r, p, l); | |
298 | r += l; | |
299 | p += l; | |
300 | } | |
301 | ||
302 | // Terminate the string | |
303 | result[length] = '\0'; | |
304 | ||
305 | ERROR: | |
306 | if (cache) | |
307 | free(cache); | |
308 | ||
309 | return result; | |
310 | } | |
311 | ||
c81287d2 MT |
312 | static unsigned int pakfire_chrcnt(const char* s, char delim) { |
313 | size_t length = strlen(s); | |
603ca1e0 | 314 | |
c81287d2 | 315 | unsigned int count = 0; |
603ca1e0 | 316 | |
c81287d2 MT |
317 | for (unsigned int i = 0; i < length; i++) |
318 | if (s[i] == delim) | |
603ca1e0 | 319 | count++; |
c81287d2 MT |
320 | |
321 | return count; | |
322 | } | |
323 | ||
324 | char** pakfire_split_string(const char* s, char delim) { | |
325 | char** array = NULL; | |
326 | ||
327 | if (!s) { | |
328 | errno = EINVAL; | |
329 | return NULL; | |
603ca1e0 MT |
330 | } |
331 | ||
c81287d2 MT |
332 | // Count how often we need to split |
333 | unsigned int count = pakfire_chrcnt(s, delim) + 1; | |
334 | ||
335 | // Allocate array | |
336 | array = calloc(count + 1, sizeof(*array)); | |
337 | if (!array) | |
338 | return NULL; | |
339 | ||
340 | // Copy string to stack | |
341 | char* p = strdupa(s); | |
342 | if (!p) | |
343 | return NULL; | |
603ca1e0 | 344 | |
603ca1e0 | 345 | unsigned int i = 0; |
603ca1e0 | 346 | while (*p) { |
c81287d2 | 347 | char* e = strchr(p, delim); |
603ca1e0 | 348 | |
c81287d2 MT |
349 | // Terminate the string |
350 | if (e) | |
351 | *e = '\0'; | |
352 | ||
353 | // Add string to the array | |
354 | array[i++] = strdup(p); | |
603ca1e0 | 355 | |
c81287d2 MT |
356 | // End loop when we reached the end |
357 | if (!e) | |
358 | break; | |
359 | ||
360 | // Or continue at the next line | |
361 | p = e + 1; | |
362 | } | |
603ca1e0 | 363 | |
c81287d2 | 364 | return array; |
603ca1e0 MT |
365 | } |
366 | ||
b03e1127 MT |
367 | char* pakfire_string_join(char** list, const char* delim) { |
368 | if (!list) | |
369 | return NULL; | |
370 | ||
371 | size_t length = 0; | |
372 | unsigned int elements = 0; | |
373 | ||
374 | // Count the number of elements and the total length | |
375 | for (char** item = list; *item; item++) { | |
376 | length += strlen(*item); | |
377 | elements++; | |
378 | } | |
379 | ||
380 | // Empty list? | |
381 | if (!elements) | |
382 | return NULL; | |
383 | ||
384 | // Add the delimiters | |
385 | length += strlen(delim) * (elements - 1); | |
386 | ||
387 | // Allocate the result string | |
388 | char* string = malloc(length + 1); | |
389 | if (!string) | |
390 | return NULL; | |
391 | ||
392 | // Pointer to where we are writing | |
393 | char* p = string; | |
394 | ||
395 | size_t bytes_left = length; | |
396 | size_t bytes_written; | |
397 | ||
398 | for (char** item = list; *item; item++) { | |
399 | bytes_written = snprintf(p, bytes_left, "%s", *item); | |
400 | ||
401 | bytes_left -= bytes_written; | |
402 | p += bytes_written; | |
403 | ||
404 | // Write the delimiter | |
405 | if (bytes_left) { | |
406 | bytes_written = snprintf(p, bytes_left, "%s", delim); | |
407 | ||
408 | bytes_left -= bytes_written; | |
409 | p += bytes_written; | |
410 | } | |
411 | } | |
412 | ||
413 | return string; | |
414 | } | |
415 | ||
cc90545a | 416 | int pakfire_format_size(char* dst, size_t length, double value) { |
df56e4ef | 417 | const char* units[] = { |
3f1f5408 MT |
418 | "%.0f ", |
419 | "%.0fk", | |
420 | "%.1fM", | |
421 | "%.1fG", | |
422 | "%.1fT", | |
df56e4ef MT |
423 | NULL |
424 | }; | |
221cc3ce MT |
425 | const char** unit = units; |
426 | ||
cc90545a MT |
427 | while (*(unit + 1) && value >= 1024.0) { |
428 | value /= 1024.0; | |
221cc3ce MT |
429 | unit++; |
430 | } | |
431 | ||
df56e4ef MT |
432 | #pragma GCC diagnostic push |
433 | #pragma GCC diagnostic ignored "-Wformat-nonliteral" | |
68d26a6a MT |
434 | return snprintf(dst, length, *unit, value); |
435 | #pragma GCC diagnostic pop | |
436 | } | |
437 | ||
438 | int pakfire_format_speed(char* dst, size_t length, double value) { | |
439 | const char* units[] = { | |
440 | "%4.0fB/s", | |
441 | "%4.0fkB/s", | |
442 | "%4.1fMB/s", | |
443 | "%4.1fGB/s", | |
444 | "%4.1fTB/s", | |
445 | NULL | |
446 | }; | |
447 | const char** unit = units; | |
448 | ||
449 | while (*(unit + 1) && value >= 1024.0) { | |
450 | value /= 1024.0; | |
451 | unit++; | |
452 | } | |
453 | ||
454 | #pragma GCC diagnostic push | |
455 | #pragma GCC diagnostic ignored "-Wformat-nonliteral" | |
456 | return snprintf(dst, length, *unit, value); | |
df56e4ef | 457 | #pragma GCC diagnostic pop |
221cc3ce MT |
458 | } |
459 | ||
526e4cec MT |
460 | #pragma GCC diagnostic push |
461 | #pragma GCC diagnostic ignored "-Wformat-nonliteral" | |
462 | static char* pakfire_strftime(const char* format, time_t t) { | |
7500066c | 463 | char string[128]; |
526e4cec MT |
464 | struct tm* tm = gmtime(&t); |
465 | ||
7500066c | 466 | strftime(string, sizeof(string) - 1, format, tm); |
526e4cec | 467 | |
5d901566 | 468 | return strdup(string); |
526e4cec MT |
469 | } |
470 | #pragma GCC diagnostic pop | |
471 | ||
472 | char* pakfire_format_date(time_t t) { | |
473 | return pakfire_strftime("%Y-%m-%d", t); | |
474 | } | |
475 | ||
99865093 | 476 | int __pakfire_path_join(char* dest, size_t length, |
384d009a | 477 | const char* first, const char* second) { |
14143513 MT |
478 | if (!first) |
479 | return snprintf(dest, length, "%s", second); | |
480 | ||
221cc3ce | 481 | if (!second) |
384d009a | 482 | return snprintf(dest, length, "%s", first); |
221cc3ce | 483 | |
14143513 MT |
484 | // Remove leading slashes from second argument |
485 | while (*second == '/') | |
486 | second++; | |
221cc3ce | 487 | |
384d009a | 488 | return snprintf(dest, length, "%s/%s", first, second); |
221cc3ce MT |
489 | } |
490 | ||
36790d86 | 491 | const char* pakfire_path_relpath(const char* root, const char* path) { |
5e3ca618 | 492 | if (pakfire_string_startswith(path, root)) |
55b57cf6 | 493 | return path + strlen(root); |
55b57cf6 MT |
494 | |
495 | return NULL; | |
496 | } | |
497 | ||
8c22118b MT |
498 | int pakfire_path_exists(const char* path) { |
499 | return !access(path, F_OK); | |
500 | } | |
501 | ||
a5e9cd4b MT |
502 | time_t pakfire_path_age(const char* path) { |
503 | struct stat st; | |
504 | ||
505 | int r = stat(path, &st); | |
506 | if (r == 0) { | |
507 | // Get current timestamp | |
508 | time_t now = time(NULL); | |
509 | ||
510 | // Return the difference since the file has been created and now | |
511 | return now - st.st_ctime; | |
512 | } | |
513 | ||
514 | return -1; | |
515 | } | |
516 | ||
05e8d3c9 | 517 | char* pakfire_basename(const char* path) { |
5d901566 | 518 | char* name = strdup(path); |
221cc3ce | 519 | |
2e9ddb9c | 520 | char* r = basename(name); |
b196ec9d | 521 | if (r) |
5d901566 | 522 | r = strdup(r); |
b196ec9d | 523 | |
f0d6233d | 524 | free(name); |
d11561f7 MT |
525 | |
526 | return r; | |
221cc3ce MT |
527 | } |
528 | ||
41a7a52f | 529 | char* pakfire_dirname(const char* path) { |
5d901566 | 530 | char* parent = strdup(path); |
221cc3ce | 531 | |
2e9ddb9c | 532 | char* r = dirname(parent); |
b196ec9d | 533 | if (r) |
5d901566 | 534 | r = strdup(r); |
b196ec9d | 535 | |
f0d6233d | 536 | free(parent); |
d11561f7 MT |
537 | |
538 | return r; | |
221cc3ce MT |
539 | } |
540 | ||
221cc3ce MT |
541 | char* pakfire_remove_trailing_newline(char* str) { |
542 | ssize_t pos = strlen(str) - 1; | |
543 | ||
544 | if (str[pos] == '\n') | |
545 | str[pos] = '\0'; | |
546 | ||
547 | return str; | |
548 | } | |
658c740d | 549 | |
a566ff7f | 550 | int pakfire_read_file_into_buffer(FILE* f, char** buffer, size_t* len) { |
3bf7e142 MT |
551 | if (!f) |
552 | return -EBADF; | |
553 | ||
554 | int r = fseek(f, 0, SEEK_END); | |
555 | if (r) | |
556 | return r; | |
557 | ||
558 | // Save length of file | |
559 | *len = ftell(f); | |
560 | ||
561 | // Go back to the start | |
562 | r = fseek(f, 0, SEEK_SET); | |
563 | if (r) | |
564 | return r; | |
565 | ||
566 | // Allocate buffer | |
547759ae | 567 | *buffer = malloc((sizeof(**buffer) * *len) + 1); |
3bf7e142 MT |
568 | if (!*buffer) |
569 | return -ENOMEM; | |
570 | ||
571 | // Read content | |
572 | fread(*buffer, *len, sizeof(**buffer), f); | |
573 | ||
574 | // Check we encountered any errors | |
575 | r = ferror(f); | |
576 | if (r) { | |
f0d6233d | 577 | free(*buffer); |
3bf7e142 MT |
578 | return r; |
579 | } | |
580 | ||
581 | // Terminate the buffer | |
582 | (*buffer)[*len] = '\0'; | |
583 | ||
584 | return 0; | |
585 | } | |
312fd26f | 586 | |
ab96da22 | 587 | char* pakfire_generate_uuid() { |
df552c32 MT |
588 | uuid_t uuid; |
589 | ||
590 | // Generate a new random value | |
591 | uuid_generate_random(uuid); | |
592 | ||
593 | char* ret = malloc(UUID_STR_LEN + 1); | |
594 | if (!ret) | |
595 | return NULL; | |
596 | ||
597 | // Convert it to string | |
598 | uuid_unparse_lower(uuid, ret); | |
599 | ||
600 | // Terminate string | |
601 | ret[UUID_STR_LEN] = '\0'; | |
602 | ||
603 | return ret; | |
604 | } | |
7ab0465c | 605 | |
457feacb MT |
606 | int pakfire_tty_is_noninteractive(void) { |
607 | const int fds[] = { | |
608 | STDIN_FILENO, | |
609 | STDOUT_FILENO, | |
610 | STDERR_FILENO, | |
611 | -1, | |
612 | }; | |
613 | ||
614 | for (const int* fd = fds; *fd >= 0; fd++) { | |
615 | if (!isatty(*fd)) | |
616 | return 1; | |
617 | } | |
618 | ||
619 | return 0; | |
620 | } | |
621 | ||
fa106e1b | 622 | char* __pakfire_hexlify(const unsigned char* digest, const size_t length) { |
7ab0465c MT |
623 | const char* hexdigits = "0123456789abcdef"; |
624 | ||
625 | char* s = malloc((length * 2) + 1); | |
626 | if (!s) | |
627 | return NULL; | |
628 | ||
629 | for (unsigned int i = 0, j = 0; i < length; i++) { | |
630 | char b = digest[i]; | |
631 | ||
632 | s[j++] = hexdigits[(b >> 4) & 0xf]; | |
633 | s[j++] = hexdigits[(b & 0x0f)]; | |
634 | } | |
635 | ||
636 | // Terminate result | |
637 | s[length * 2] = '\0'; | |
638 | ||
639 | return s; | |
640 | } | |
62b60e37 | 641 | |
d0e6b214 MT |
642 | static int parse_nibble(char nibble) { |
643 | // Handle digits | |
644 | if (nibble >= '0' && nibble <= '9') | |
645 | return nibble - '0'; | |
646 | ||
647 | // Handle lowercase letters | |
648 | if (nibble >= 'a' && nibble <= 'f') | |
649 | return 10 + nibble - 'a'; | |
650 | ||
651 | // Handle uppercase letters | |
652 | if (nibble >= 'A' && nibble <= 'F') | |
653 | return 10 + nibble - 'A'; | |
654 | ||
655 | // Error | |
656 | return -1; | |
657 | } | |
658 | ||
01f8f6b0 | 659 | int __pakfire_unhexlify(unsigned char* dst, const size_t l, const char* src) { |
d0e6b214 MT |
660 | if (!src) { |
661 | errno = EINVAL; | |
662 | return 1; | |
663 | } | |
8bfcf741 | 664 | |
d0e6b214 MT |
665 | // Determine input length |
666 | const size_t length = strlen(src); | |
667 | ||
668 | for (unsigned int i = 0, j = 0; i < length && j < l; i += 2, j++) { | |
669 | int h = parse_nibble(src[i]); | |
670 | int l = parse_nibble(src[i+1]); | |
671 | ||
672 | // Check for invalid input | |
673 | if (h < 0 || l < 0) { | |
674 | errno = EINVAL; | |
8bfcf741 | 675 | return 1; |
d0e6b214 | 676 | } |
8bfcf741 | 677 | |
d0e6b214 MT |
678 | // Store result |
679 | dst[j] = h << 4 | l; | |
8bfcf741 MT |
680 | } |
681 | ||
682 | return 0; | |
683 | } | |
684 | ||
a29c7190 | 685 | int pakfire_mkparentdir(const char* path, mode_t mode) { |
62b60e37 MT |
686 | int r; |
687 | ||
688 | char* dirname = pakfire_dirname(path); | |
689 | if (!dirname) | |
690 | return 1; | |
691 | ||
692 | // We have arrived at the top of the tree | |
aba60128 | 693 | if (*dirname == '.' || strcmp(dirname, "/") == 0) |
62b60e37 MT |
694 | return 0; |
695 | ||
696 | // Ensure the parent directory exists | |
4d47c163 | 697 | r = pakfire_mkparentdir(dirname, mode); |
aba60128 | 698 | if (r) |
62b60e37 | 699 | goto END; |
62b60e37 MT |
700 | |
701 | // Create this directory | |
4d47c163 | 702 | r = mkdir(dirname, mode); |
62b60e37 | 703 | |
aba60128 MT |
704 | // Ignore when the directory already exists |
705 | if (r && errno == EEXIST) | |
706 | r = 0; | |
707 | ||
62b60e37 MT |
708 | END: |
709 | free(dirname); | |
710 | ||
711 | return r; | |
712 | } | |
713 | ||
4d47c163 MT |
714 | int pakfire_mkdir(const char* path, mode_t mode) { |
715 | int r = pakfire_mkparentdir(path, mode); | |
716 | if (r) | |
717 | return r; | |
718 | ||
719 | // Finally, create the directory we want | |
720 | return mkdir(path, mode); | |
721 | } | |
722 | ||
62b60e37 | 723 | FILE* pakfire_mktemp(char* path) { |
4d47c163 | 724 | int r = pakfire_mkparentdir(path, 0); |
aba60128 MT |
725 | if (r) |
726 | return NULL; | |
62b60e37 MT |
727 | |
728 | // Create a temporary result file | |
729 | int fd = mkostemp(path, O_CLOEXEC); | |
730 | if (fd < 0) | |
731 | return NULL; | |
732 | ||
733 | // Re-open as file handle | |
734 | return fdopen(fd, "w+"); | |
735 | } | |
0351af83 | 736 | |
e75d1e3c | 737 | char* pakfire_mkdtemp(char* path) { |
4d47c163 | 738 | int r = pakfire_mkparentdir(path, 0); |
e75d1e3c MT |
739 | if (r) |
740 | return NULL; | |
741 | ||
742 | return mkdtemp(path); | |
743 | } | |
744 | ||
745 | static int _unlink(const char* path, const struct stat* stat, int typeflag, struct FTW* ftwbuf) { | |
746 | return remove(path); | |
747 | } | |
748 | ||
749 | int pakfire_rmtree(const char* path, int flags) { | |
750 | int r = nftw(path, _unlink, 64, flags|FTW_DEPTH|FTW_PHYS); | |
751 | ||
752 | // Ignore if path didn't exist | |
753 | if (r < 0 && errno == ENOENT) | |
754 | r = 0; | |
755 | ||
756 | return r; | |
757 | } | |
758 | ||
88124868 MT |
759 | // Archive Stuff |
760 | ||
761 | int pakfire_archive_copy_data(struct archive* src, struct archive* dst, | |
762 | struct archive_entry* entry) { | |
763 | const void* buffer; | |
764 | size_t size; | |
765 | off_t offset; | |
766 | int r; | |
767 | ||
768 | for (;;) { | |
769 | // Read a block of data | |
770 | r = archive_read_data_block(src, &buffer, &size, &offset); | |
771 | if (r == ARCHIVE_EOF) | |
772 | return ARCHIVE_OK; | |
773 | else if (r) | |
774 | return r; | |
775 | ||
776 | // Write the read block of data | |
777 | r = archive_write_data(dst, buffer, size); | |
778 | if (r < 0) | |
779 | return r; | |
780 | } | |
781 | } | |
782 | ||
ac4c607b | 783 | int pakfire_archive_copy_data_to_buffer(struct pakfire* pakfire, struct archive* a, |
53f5745e MT |
784 | struct archive_entry* entry, char** data, size_t* data_size) { |
785 | *data = NULL; | |
786 | *data_size = 0; | |
787 | ||
788 | size_t required_size = archive_entry_size(entry); | |
789 | if (!required_size) | |
790 | return 0; | |
791 | ||
792 | // Allocate a block of the required size | |
b38802ab | 793 | *data = calloc(1, required_size + 1); |
53f5745e MT |
794 | if (!*data) |
795 | return ENOMEM; | |
796 | ||
797 | ssize_t bytes_read = archive_read_data(a, *data, required_size); | |
798 | if (bytes_read < 0) { | |
799 | ERROR(pakfire, "Could not read from archive: %s\n", archive_error_string(a)); | |
800 | free(*data); | |
801 | return 1; | |
802 | } | |
803 | ||
804 | *data_size = bytes_read; | |
805 | ||
806 | return 0; | |
807 | } | |
808 | ||
0351af83 MT |
809 | // JSON Stuff |
810 | ||
ac4c607b | 811 | static struct json_object* pakfire_json_parse(struct pakfire* pakfire, FILE* f) { |
0351af83 MT |
812 | struct json_tokener* tokener = NULL; |
813 | struct json_object* json = NULL; | |
814 | char* buffer = NULL; | |
815 | size_t length; | |
816 | ||
817 | // Read everything into memory | |
818 | int r = pakfire_read_file_into_buffer(f, &buffer, &length); | |
819 | if (r) | |
820 | goto ERROR; | |
821 | ||
822 | // Create tokener | |
823 | tokener = json_tokener_new(); | |
824 | if (!tokener) { | |
b1772bfb | 825 | ERROR(pakfire, "Could not allocate JSON tokener: %m\n"); |
0351af83 MT |
826 | goto ERROR; |
827 | } | |
828 | ||
829 | // Parse JSON from path | |
830 | json = json_tokener_parse_ex(tokener, buffer, length); | |
831 | if (!json) { | |
832 | enum json_tokener_error error = json_tokener_get_error(tokener); | |
833 | ||
834 | ERROR(pakfire, "JSON parsing error: %s\n", json_tokener_error_desc(error)); | |
835 | goto ERROR; | |
836 | } | |
837 | ||
838 | // Log what we have parsed | |
839 | DEBUG(pakfire, "Parsed JSON:\n%s\n", | |
840 | json_object_to_json_string_ext(json, | |
841 | JSON_C_TO_STRING_SPACED | JSON_C_TO_STRING_PRETTY) | |
842 | ); | |
843 | ||
844 | ERROR: | |
845 | if (buffer) | |
846 | free(buffer); | |
847 | ||
848 | if (tokener) | |
849 | json_tokener_free(tokener); | |
850 | ||
851 | return json; | |
852 | } | |
853 | ||
ac4c607b | 854 | struct json_object* pakfire_json_parse_from_file(struct pakfire* pakfire, const char* path) { |
0351af83 MT |
855 | FILE* f = fopen(path, "r"); |
856 | if (!f) | |
857 | return NULL; | |
858 | ||
859 | struct json_object* json = pakfire_json_parse(pakfire, f); | |
860 | fclose(f); | |
861 | ||
862 | return json; | |
863 | } | |
563e9600 MT |
864 | |
865 | // Time Stuff | |
866 | ||
867 | static void timespec_normalize(struct timespec* t) { | |
868 | while (t->tv_nsec >= NSEC_PER_SEC) { | |
869 | t->tv_sec++; | |
870 | t->tv_nsec -= NSEC_PER_SEC; | |
871 | } | |
872 | ||
873 | while (t->tv_nsec <= -NSEC_PER_SEC) { | |
874 | t->tv_sec--; | |
875 | t->tv_nsec += NSEC_PER_SEC; | |
876 | } | |
877 | } | |
878 | ||
879 | struct timespec timespec_add(const struct timespec* t1, const struct timespec* t2) { | |
880 | struct timespec r = { | |
881 | .tv_sec = t1->tv_sec + t2->tv_sec, | |
882 | .tv_nsec = t2->tv_nsec + t2->tv_nsec, | |
883 | }; | |
884 | ||
885 | // Correct any negative values | |
886 | timespec_normalize(&r); | |
887 | ||
888 | return r; | |
889 | } | |
890 | ||
891 | struct timespec timespec_from_ms(int milliseconds) { | |
892 | struct timespec t = { | |
893 | .tv_sec = (milliseconds / 1000), | |
894 | .tv_nsec = (milliseconds % 1000) * 1000000, | |
895 | }; | |
896 | ||
897 | return t; | |
898 | } | |
899 | ||
900 | int timespec_lt(struct timespec* t1, struct timespec* t2) { | |
901 | return ( | |
902 | t1->tv_sec < t2->tv_sec || | |
903 | (t1->tv_sec == t2->tv_sec && t1->tv_nsec < t2->tv_nsec) | |
904 | ); | |
905 | } | |
6582a143 MT |
906 | |
907 | // Resource Limits | |
908 | ||
ac4c607b | 909 | int pakfire_rlimit_set(struct pakfire* pakfire, int limit) { |
6582a143 MT |
910 | struct rlimit rl; |
911 | ||
912 | // Sanity check | |
913 | if (limit < 3) { | |
914 | errno = EINVAL; | |
915 | return 1; | |
916 | } | |
917 | ||
918 | // Fetch current configuration | |
919 | if (getrlimit(RLIMIT_NOFILE, &rl) < 0) { | |
b1772bfb | 920 | ERROR(pakfire, "Could not read RLIMIT_NOFILE: %m\n"); |
6582a143 MT |
921 | return 1; |
922 | } | |
923 | ||
924 | // Do not attempt to set higher than maximum | |
925 | if ((long unsigned int)limit > rl.rlim_max) | |
926 | limit = rl.rlim_max; | |
927 | ||
928 | rl.rlim_cur = limit; | |
929 | ||
930 | // Set the new limit | |
931 | if (setrlimit(RLIMIT_NOFILE, &rl) < 0) { | |
b1772bfb | 932 | ERROR(pakfire, "Could not set RLIMIT_NOFILE to %lu: %m\n", rl.rlim_cur); |
6582a143 MT |
933 | return 1; |
934 | } | |
935 | ||
936 | DEBUG(pakfire, "RLIMIT_NOFILE set to %d\n", limit); | |
937 | ||
938 | return 0; | |
939 | } | |
940 | ||
941 | /* | |
942 | Resets RLIMIT_NOFILE to FD_SETSIZE (e.g. 1024) | |
943 | for compatibility with software that uses select() | |
944 | */ | |
ac4c607b | 945 | int pakfire_rlimit_reset_nofile(struct pakfire* pakfire) { |
6582a143 MT |
946 | return pakfire_rlimit_set(pakfire, FD_SETSIZE); |
947 | } |