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