1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
6 #include "alloc-util.h"
10 #include "parse-util.h"
11 #include "process-util.h"
12 #include "procfs-util.h"
13 #include "stdio-util.h"
14 #include "string-util.h"
16 int procfs_get_pid_max(uint64_t *ret
) {
17 _cleanup_free_
char *value
= NULL
;
22 r
= read_one_line_file("/proc/sys/kernel/pid_max", &value
);
26 return safe_atou64(value
, ret
);
29 int procfs_get_threads_max(uint64_t *ret
) {
30 _cleanup_free_
char *value
= NULL
;
35 r
= read_one_line_file("/proc/sys/kernel/threads-max", &value
);
39 return safe_atou64(value
, ret
);
42 int procfs_tasks_set_limit(uint64_t limit
) {
43 char buffer
[DECIMAL_STR_MAX(uint64_t)+1];
47 if (limit
== 0) /* This makes no sense, we are userspace and hence count as tasks too, and we want to live,
48 * hence the limit conceptually has to be above 0. Also, most likely if anyone asks for a zero
49 * limit they probably mean "no limit", hence let's better refuse this to avoid
53 /* The Linux kernel doesn't allow this value to go below 20, hence don't allow this either, higher values than
54 * TASKS_MAX are not accepted by the pid_max sysctl. We'll treat anything this high as "unbounded" and hence
55 * set it to the maximum. */
56 limit
= CLAMP(limit
, 20U, TASKS_MAX
);
58 r
= procfs_get_pid_max(&pid_max
);
62 /* As pid_max is about the numeric pid_t range we'll bump it if necessary, but only ever increase it, never
63 * decrease it, as threads-max is the much more relevant sysctl. */
64 if (limit
> pid_max
-1) {
65 sprintf(buffer
, "%" PRIu64
, limit
+1); /* Add one, since PID 0 is not a valid PID */
66 r
= write_string_file("/proc/sys/kernel/pid_max", buffer
, WRITE_STRING_FILE_DISABLE_BUFFER
);
71 sprintf(buffer
, "%" PRIu64
, limit
);
72 r
= write_string_file("/proc/sys/kernel/threads-max", buffer
, WRITE_STRING_FILE_DISABLE_BUFFER
);
76 /* Hmm, we couldn't write this? If so, maybe it was already set properly? In that case let's not
77 * generate an error */
79 if (procfs_get_threads_max(&threads_max
) < 0)
80 return r
; /* return original error */
82 if (MIN(pid_max
- 1, threads_max
) != limit
)
83 return r
; /* return original error */
85 /* Yay! Value set already matches what we were trying to set, hence consider this a success. */
91 int procfs_tasks_get_current(uint64_t *ret
) {
92 _cleanup_free_
char *value
= NULL
;
99 r
= read_one_line_file("/proc/loadavg", &value
);
103 /* Look for the second part of the fourth field, which is separated by a slash from the first part. None of the
104 * earlier fields use a slash, hence let's use this to find the right spot. */
105 p
= strchr(value
, '/');
110 n
= strspn(p
, DIGITS
);
111 nr
= strndupa_safe(p
, n
);
113 return safe_atou64(nr
, ret
);
116 static uint64_t calc_gcd64(uint64_t a
, uint64_t b
) {
130 int procfs_cpu_get_usage(nsec_t
*ret
) {
131 _cleanup_free_
char *first_line
= NULL
;
132 unsigned long user_ticks
, nice_ticks
, system_ticks
, irq_ticks
, softirq_ticks
,
133 guest_ticks
= 0, guest_nice_ticks
= 0;
134 long ticks_per_second
;
135 uint64_t sum
, gcd
, a
, b
;
141 r
= read_one_line_file("/proc/stat", &first_line
);
145 p
= first_word(first_line
, "cpu");
149 if (sscanf(p
, "%lu %lu %lu %*u %*u %lu %lu %*u %lu %lu",
156 &guest_nice_ticks
) < 5) /* we only insist on the first five fields */
159 ticks_per_second
= sysconf(_SC_CLK_TCK
);
160 if (ticks_per_second
< 0)
162 assert(ticks_per_second
> 0);
164 sum
= (uint64_t) user_ticks
+ (uint64_t) nice_ticks
+ (uint64_t) system_ticks
+
165 (uint64_t) irq_ticks
+ (uint64_t) softirq_ticks
+
166 (uint64_t) guest_ticks
+ (uint64_t) guest_nice_ticks
;
168 /* Let's reduce this fraction before we apply it to avoid overflows when converting this to μsec */
169 gcd
= calc_gcd64(NSEC_PER_SEC
, ticks_per_second
);
171 a
= (uint64_t) NSEC_PER_SEC
/ gcd
;
172 b
= (uint64_t) ticks_per_second
/ gcd
;
174 *ret
= DIV_ROUND_UP((nsec_t
) sum
* (nsec_t
) a
, (nsec_t
) b
);
178 int convert_meminfo_value_to_uint64_bytes(const char *word
, uint64_t *ret
) {
179 _cleanup_free_
char *w
= NULL
;
192 /* Determine length of numeric value */
193 n
= strspn(w
, WHITESPACE
);
195 n
= strspn(digits
, DIGITS
);
200 /* Ensure the line ends in " kB" */
201 n
= strspn(e
, WHITESPACE
);
204 if (!streq(e
+ n
, "kB"))
208 r
= safe_atou64(digits
, &v
);
214 if (v
> UINT64_MAX
/1024)
221 int procfs_memory_get(uint64_t *ret_total
, uint64_t *ret_used
) {
222 uint64_t mem_total
= UINT64_MAX
, mem_available
= UINT64_MAX
;
223 _cleanup_fclose_
FILE *f
= NULL
;
226 f
= fopen("/proc/meminfo", "re");
231 _cleanup_free_
char *line
= NULL
;
235 r
= read_line(f
, LONG_LINE_MAX
, &line
);
239 return -EINVAL
; /* EOF: Couldn't find one or both fields? */
241 p
= first_word(line
, "MemTotal:");
245 p
= first_word(line
, "MemAvailable:");
252 r
= convert_meminfo_value_to_uint64_bytes(p
, v
);
256 if (mem_total
!= UINT64_MAX
&& mem_available
!= UINT64_MAX
)
260 if (mem_available
> mem_total
)
264 *ret_total
= mem_total
;
266 *ret_used
= mem_total
- mem_available
;