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 : 174 : 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 [ + + ]: 544 : for (f = 0; f < nfields; f++) {
58 [ + + ]: 445 : if (fieldlen != fields[f].len)
59 : 337 : continue;
60 [ + + ]: 108 : if (strncmp(fieldname, fields[f].name, fields[f].len) != 0)
61 : 33 : continue;
62 : :
63 : 75 : return &fields[f];
64 : : }
65 : :
66 : 99 : return NULL;
67 : : }
68 : :
69 : : static uint64_t
70 : 24 : meminfo_sum_fields(const struct meminfo_field *fields, const size_t nfields)
71 : : {
72 : 24 : uint64_t sum = 0;
73 : : size_t f;
74 : :
75 [ + + ]: 96 : for (f = 0; f < nfields; f++)
76 : 72 : sum += fields[f].value;
77 : :
78 : 24 : return sum;
79 : : }
80 : :
81 : : enum meminfo_error_code
82 : 28 : 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 : 28 : 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 : 28 : const int want_tags = DPKG_BIT(array_count(fields)) - 1;
94 : 28 : int seen_tags = 0;
95 : :
96 : 28 : *val = 0;
97 : :
98 : 28 : fd = open(filename, O_RDONLY);
99 [ + + ]: 28 : if (fd < 0)
100 : 1 : return MEMINFO_NO_FILE;
101 : :
102 : 27 : bytes = fd_read(fd, buf, sizeof(buf));
103 : 27 : close(fd);
104 : :
105 [ + + ]: 27 : if (bytes <= 0)
106 : 1 : return MEMINFO_NO_DATA;
107 : :
108 : 26 : buf[bytes] = '\0';
109 : :
110 : 26 : str = buf;
111 : 149 : while (1) {
112 : : struct meminfo_field *field;
113 : : char *end;
114 : :
115 : 175 : end = strchr(str, ':');
116 [ + + ]: 175 : if (end == 0)
117 : 1 : break;
118 : :
119 : 174 : field = meminfo_find_field(fields, array_count(fields),
120 : : str, end - str);
121 [ + + ]: 174 : if (field) {
122 : : intmax_t num;
123 : :
124 : 75 : str = end + 1;
125 : 75 : errno = 0;
126 : 75 : num = strtoimax(str, &end, 10);
127 [ - + ]: 75 : if (num <= 0)
128 : 1 : return MEMINFO_INT_NEG;
129 [ - + - - ]: 75 : if ((num == INTMAX_MAX) && errno == ERANGE)
130 : 0 : return MEMINFO_INT_MAX;
131 : : /* It should end with ' kB\n'. */
132 [ + + + - : 75 : if (end[0] != ' ' || end[1] != 'k' || end[2] != 'B')
- + ]
133 : 1 : return MEMINFO_NO_UNIT;
134 : :
135 : : /* This should not overflow, but just in case. */
136 [ + - ]: 74 : if (num < (INTMAX_MAX / 1024))
137 : 74 : num *= 1024;
138 : :
139 : 74 : field->value = num;
140 : 74 : seen_tags |= field->tag;
141 : : }
142 : :
143 [ + + ]: 173 : if (seen_tags == want_tags)
144 : 24 : break;
145 : :
146 : 149 : end = strchr(end + 1, '\n');
147 [ - + ]: 149 : if (end == 0)
148 : 0 : break;
149 : 149 : str = end + 1;
150 : : }
151 : :
152 [ + + ]: 25 : if (seen_tags != want_tags)
153 : 1 : return MEMINFO_NO_INFO;
154 : :
155 : 24 : *val = meminfo_sum_fields(fields, array_count(fields));
156 : 24 : return MEMINFO_OK;
157 : : }
158 : :
159 : : enum meminfo_error_code
160 : 23 : meminfo_get_available(uint64_t *val)
161 : : {
162 : : #ifdef __linux__
163 : 23 : return meminfo_get_available_from_file("/proc/meminfo", val);
164 : : #else
165 : : return MEMINFO_NO_FILE;
166 : : #endif
167 : : }
|