]> git.ipfire.org Git - pakfire.git/blame - src/libpakfire/util.c
util: Add test for pakfire_string_endswith
[pakfire.git] / src / libpakfire / util.c
CommitLineData
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
50static 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 69Id 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 136void 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 159int 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 169int 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
179int pakfire_string_matches(const char* s, const char* pattern) {
180 return !!strstr(s, pattern);
181}
182
5c76be2c
MT
183char* 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 207int 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 234char* 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
317ERROR:
318 if (cache)
319 free(cache);
320
321 return result;
322}
323
c81287d2
MT
324static 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
336char** 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
379char* 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 428int 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
450int 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"
474static 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
484char* pakfire_format_date(time_t t) {
485 return pakfire_strftime("%Y-%m-%d", t);
486}
487
5f2ed202
MT
488int __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 509int __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 524const 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
531int pakfire_path_exists(const char* path) {
532 return !access(path, F_OK);
533}
534
a5e9cd4b
MT
535time_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 550char* 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 562char* 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
574char* 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 583int 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 620char* 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
639int 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 655char* __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
675static 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 692int __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 718int 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
741END:
742 free(dirname);
743
744 return r;
745}
746
4d47c163
MT
747int 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 756FILE* 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 770char* 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
778static int _unlink(const char* path, const struct stat* stat, int typeflag, struct FTW* ftwbuf) {
779 return remove(path);
780}
781
782int 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
794size_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
814int 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 836int 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 864static 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
897ERROR:
898 if (buffer)
899 free(buffer);
900
901 if (tokener)
902 json_tokener_free(tokener);
903
904 return json;
905}
906
ac4c607b 907struct 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
920static 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
932struct 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
944struct 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
953int 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 962int 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 998int pakfire_rlimit_reset_nofile(struct pakfire* pakfire) {
6582a143
MT
999 return pakfire_rlimit_set(pakfire, FD_SETSIZE);
1000}