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