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