Branch data Line data Source code
1 : : /*
2 : : * libdpkg - Debian packaging suite library routines
3 : : * meminfo.c - system memory information functions
4 : : *
5 : : * Copyright © 2021 Sebastian Andrzej Siewior <sebastian@breakpoint.cc>
6 : : * Copyright © 2021-2022 Guillem Jover <guillem@debian.org>
7 : : *
8 : : * This is free software; you can redistribute it and/or modify
9 : : * it under the terms of the GNU General Public License as published by
10 : : * the Free Software Foundation; either version 2 of the License, or
11 : : * (at your option) any later version.
12 : : *
13 : : * This is distributed in the hope that it will be useful,
14 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 : : * GNU General Public License for more details.
17 : : *
18 : : * You should have received a copy of the GNU General Public License
19 : : * along with this program. If not, see <https://www.gnu.org/licenses/>.
20 : : */
21 : :
22 : : #include <config.h>
23 : : #include <compat.h>
24 : :
25 : : #include <sys/stat.h>
26 : :
27 : : #include <errno.h>
28 : : #include <inttypes.h>
29 : : #include <fcntl.h>
30 : : #include <string.h>
31 : : #include <unistd.h>
32 : :
33 : : #include <dpkg/dpkg.h>
34 : : #include <dpkg/fdio.h>
35 : : #include <dpkg/meminfo.h>
36 : :
37 : : /*
38 : : * An estimate of how much memory is available. Swap will not be used, the
39 : : * page cache may be purged, not everything will be reclaimed that might be
40 : : * reclaimed, watermarks are considered.
41 : : */
42 : :
43 : : struct meminfo_field {
44 : : const char *name;
45 : : ssize_t len;
46 : : int tag;
47 : : uint64_t value;
48 : : };
49 : : #define MEMINFO_FIELD(name, tag) name, sizeof(name) - 1, tag, 0
50 : :
51 : : static struct meminfo_field *
52 : 129 : meminfo_find_field(struct meminfo_field *fields, const size_t nfields,
53 : : const char *fieldname, const ssize_t fieldlen)
54 : : {
55 : : size_t f;
56 : :
57 [ + + ]: 418 : for (f = 0; f < nfields; f++) {
58 [ + + ]: 337 : if (fieldlen != fields[f].len)
59 : 265 : continue;
60 [ + + ]: 72 : if (strncmp(fieldname, fields[f].name, fields[f].len) != 0)
61 : 24 : continue;
62 : :
63 : 48 : return &fields[f];
64 : : }
65 : :
66 : 81 : return NULL;
67 : : }
68 : :
69 : : static uint64_t
70 : 15 : meminfo_sum_fields(const struct meminfo_field *fields, const size_t nfields)
71 : : {
72 : 15 : uint64_t sum = 0;
73 : : size_t f;
74 : :
75 [ + + ]: 60 : for (f = 0; f < nfields; f++)
76 : 45 : sum += fields[f].value;
77 : :
78 : 15 : return sum;
79 : : }
80 : :
81 : : int
82 : 19 : meminfo_get_available_from_file(const char *filename, uint64_t *val)
83 : : {
84 : : char buf[4096];
85 : : char *str;
86 : : ssize_t bytes;
87 : : int fd;
88 : 19 : struct meminfo_field fields[] = {
89 : : { MEMINFO_FIELD("MemFree", DPKG_BIT(0)) },
90 : : { MEMINFO_FIELD("Buffers", DPKG_BIT(1)) },
91 : : { MEMINFO_FIELD("Cached", DPKG_BIT(2)) },
92 : : };
93 : 19 : const int want_tags = DPKG_BIT(array_count(fields)) - 1;
94 : 19 : int seen_tags = 0;
95 : :
96 : 19 : *val = 0;
97 : :
98 : 19 : fd = open(filename, O_RDONLY);
99 [ + + ]: 19 : if (fd < 0)
100 : 1 : return MEMINFO_NO_FILE;
101 : :
102 : 18 : bytes = fd_read(fd, buf, sizeof(buf));
103 : 18 : close(fd);
104 : :
105 [ + + ]: 18 : if (bytes <= 0)
106 : 1 : return MEMINFO_NO_DATA;
107 : :
108 : 17 : buf[bytes] = '\0';
109 : :
110 : 17 : str = buf;
111 : 113 : while (1) {
112 : : struct meminfo_field *field;
113 : : char *end;
114 : :
115 : 130 : end = strchr(str, ':');
116 [ + + ]: 130 : if (end == 0)
117 : 1 : break;
118 : :
119 : 129 : field = meminfo_find_field(fields, array_count(fields),
120 : : str, end - str);
121 [ + + ]: 129 : if (field) {
122 : : intmax_t num;
123 : :
124 : 48 : str = end + 1;
125 : 48 : errno = 0;
126 : 48 : num = strtoimax(str, &end, 10);
127 [ - + ]: 48 : if (num <= 0)
128 : 1 : return MEMINFO_INT_NEG;
129 [ - + - - ]: 48 : if ((num == INTMAX_MAX) && errno == ERANGE)
130 : 0 : return MEMINFO_INT_MAX;
131 : : /* It should end with ' kB\n'. */
132 [ + + + - ]: 48 : if (*end != ' ' || *(end + 1) != 'k' ||
133 [ - + ]: 47 : *(end + 2) != 'B')
134 : 1 : return MEMINFO_NO_UNIT;
135 : :
136 : : /* This should not overflow, but just in case. */
137 [ + - ]: 47 : if (num < (INTMAX_MAX / 1024))
138 : 47 : num *= 1024;
139 : :
140 : 47 : field->value = num;
141 : 47 : seen_tags |= field->tag;
142 : : }
143 : :
144 [ + + ]: 128 : if (seen_tags == want_tags)
145 : 15 : break;
146 : :
147 : 113 : end = strchr(end + 1, '\n');
148 [ - + ]: 113 : if (end == 0)
149 : 0 : break;
150 : 113 : str = end + 1;
151 : : }
152 : :
153 [ + + ]: 16 : if (seen_tags != want_tags)
154 : 1 : return MEMINFO_NO_INFO;
155 : :
156 : 15 : *val = meminfo_sum_fields(fields, array_count(fields));
157 : 15 : return MEMINFO_OK;
158 : : }
159 : :
160 : : int
161 : 14 : meminfo_get_available(uint64_t *val)
162 : : {
163 : : #ifdef __linux__
164 : 14 : return meminfo_get_available_from_file("/proc/meminfo", val);
165 : : #else
166 : : return MEMINFO_NO_FILE;
167 : : #endif
168 : : }
|