]> git.proxmox.com Git - lxcfs.git/blob - debian/patches/0001-bindings-calculate-uptime-via-proc-pid-stat.patch
242bd27302e48d0af002b98211e3f7d58cfd4566
[lxcfs.git] / debian / patches / 0001-bindings-calculate-uptime-via-proc-pid-stat.patch
1 From c3d48d76499b8f18e747d068ea60b95875b3bb3d Mon Sep 17 00:00:00 2001
2 From: Christian Brauner <christian.brauner@ubuntu.com>
3 Date: Fri, 26 May 2017 05:06:30 +0200
4 Subject: [PATCH lxcfs 1/2] bindings: calculate uptime via proc/<pid>/stat
5
6 Closes #165.
7 Closes #184.
8
9 Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
10 ---
11 bindings.c | 189 ++++++++++++++++++++++++++++++++++++++++++++++++++++---------
12 1 file changed, 162 insertions(+), 27 deletions(-)
13
14 diff --git a/bindings.c b/bindings.c
15 index 6387012..73401bf 100644
16 --- a/bindings.c
17 +++ b/bindings.c
18 @@ -8,14 +8,17 @@
19
20 #define FUSE_USE_VERSION 26
21
22 +#define __STDC_FORMAT_MACROS
23 #include <dirent.h>
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <fuse.h>
27 +#include <inttypes.h>
28 #include <libgen.h>
29 #include <pthread.h>
30 #include <sched.h>
31 #include <stdbool.h>
32 +#include <stdint.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 @@ -30,11 +33,15 @@
37 #include <sys/param.h>
38 #include <sys/socket.h>
39 #include <sys/syscall.h>
40 +#include <sys/sysinfo.h>
41 #include <sys/vfs.h>
42
43 #include "bindings.h"
44 #include "config.h" // for VERSION
45
46 +/* Maximum number for 64 bit integer is a string with 21 digits: 2^64 - 1 = 21 */
47 +#define LXCFS_NUMSTRLEN64 21
48 +
49 /* Define pivot_root() if missing from the C library */
50 #ifndef HAVE_PIVOT_ROOT
51 static int pivot_root(const char * new_root, const char * put_old)
52 @@ -3454,25 +3461,152 @@ err:
53 return rv;
54 }
55
56 -static long int getreaperctime(pid_t pid)
57 +static uint64_t get_reaper_start_time(pid_t pid)
58 {
59 - char fnam[100];
60 - struct stat sb;
61 int ret;
62 + FILE *f;
63 + uint64_t starttime;
64 + /* strlen("/proc/") = 6
65 + * +
66 + * LXCFS_NUMSTRLEN64
67 + * +
68 + * strlen("/stat") = 5
69 + * +
70 + * \0 = 1
71 + * */
72 +#define __PROC_PID_STAT_LEN (6 + LXCFS_NUMSTRLEN64 + 5 + 1)
73 + char path[__PROC_PID_STAT_LEN];
74 pid_t qpid;
75
76 qpid = lookup_initpid_in_store(pid);
77 - if (qpid <= 0)
78 + if (qpid <= 0) {
79 + /* Caller can check for EINVAL on 0. */
80 + errno = EINVAL;
81 + return 0;
82 + }
83 +
84 + ret = snprintf(path, __PROC_PID_STAT_LEN, "/proc/%d/stat", qpid);
85 + if (ret < 0 || ret >= __PROC_PID_STAT_LEN) {
86 + /* Caller can check for EINVAL on 0. */
87 + errno = EINVAL;
88 + return 0;
89 + }
90 +
91 + f = fopen(path, "r");
92 + if (!f) {
93 + /* Caller can check for EINVAL on 0. */
94 + errno = EINVAL;
95 + return 0;
96 + }
97 +
98 + /* Note that the *scanf() argument supression requires that length
99 + * modifiers such as "l" are omitted. Otherwise some compilers will yell
100 + * at us. It's like telling someone you're not married and then asking
101 + * if you can bring your wife to the party.
102 + */
103 + ret = fscanf(f, "%*d " /* (1) pid %d */
104 + "%*s " /* (2) comm %s */
105 + "%*c " /* (3) state %c */
106 + "%*d " /* (4) ppid %d */
107 + "%*d " /* (5) pgrp %d */
108 + "%*d " /* (6) session %d */
109 + "%*d " /* (7) tty_nr %d */
110 + "%*d " /* (8) tpgid %d */
111 + "%*u " /* (9) flags %u */
112 + "%*u " /* (10) minflt %lu */
113 + "%*u " /* (11) cminflt %lu */
114 + "%*u " /* (12) majflt %lu */
115 + "%*u " /* (13) cmajflt %lu */
116 + "%*u " /* (14) utime %lu */
117 + "%*u " /* (15) stime %lu */
118 + "%*d " /* (16) cutime %ld */
119 + "%*d " /* (17) cstime %ld */
120 + "%*d " /* (18) priority %ld */
121 + "%*d " /* (19) nice %ld */
122 + "%*d " /* (20) num_threads %ld */
123 + "%*d " /* (21) itrealvalue %ld */
124 + "%" PRIu64, /* (22) starttime %llu */
125 + &starttime);
126 + if (ret != 1) {
127 + fclose(f);
128 + /* Caller can check for EINVAL on 0. */
129 + errno = EINVAL;
130 + return 0;
131 + }
132 +
133 + fclose(f);
134 +
135 + errno = 0;
136 + return starttime;
137 +}
138 +
139 +static uint64_t get_reaper_start_time_in_sec(pid_t pid)
140 +{
141 + uint64_t clockticks;
142 + int64_t ticks_per_sec;
143 +
144 + clockticks = get_reaper_start_time(pid);
145 + if (clockticks == 0 && errno == EINVAL) {
146 + lxcfs_debug("failed to retrieve start time of pid %d\n", pid);
147 return 0;
148 + }
149
150 - ret = snprintf(fnam, 100, "/proc/%d", qpid);
151 - if (ret < 0 || ret >= 100)
152 + ticks_per_sec = sysconf(_SC_CLK_TCK);
153 + if (ticks_per_sec < 0 && errno == EINVAL) {
154 + lxcfs_debug(
155 + "%s\n",
156 + "failed to determine number of clock ticks in a second");
157 return 0;
158 + }
159 +
160 + return (clockticks /= ticks_per_sec);
161 +}
162 +
163 +static uint64_t get_reaper_age(pid_t pid)
164 +{
165 + uint64_t procstart, uptime, procage;
166
167 - if (lstat(fnam, &sb) < 0)
168 + /* We need to substract the time the process has started since system
169 + * boot minus the time when the system has started to get the actual
170 + * reaper age.
171 + */
172 + procstart = get_reaper_start_time_in_sec(pid);
173 + procage = procstart;
174 + if (procstart > 0) {
175 + int ret;
176 + struct timespec spec;
177 +
178 + ret = clock_gettime(CLOCK_BOOTTIME, &spec);
179 + if (ret < 0)
180 + return 0;
181 + /* We could make this more precise here by using the tv_nsec
182 + * field in the timespec struct and convert it to milliseconds
183 + * and then create a double for the seconds and milliseconds but
184 + * that seems more work than it is worth.
185 + */
186 + uptime = spec.tv_sec;
187 + procage = uptime - procstart;
188 + }
189 +
190 + return procage;
191 +}
192 +
193 +static uint64_t get_reaper_btime(pid)
194 +{
195 + int ret;
196 + struct sysinfo sys;
197 + uint64_t procstart;
198 + uint64_t uptime;
199 +
200 + ret = sysinfo(&sys);
201 + if (ret < 0) {
202 + lxcfs_debug("%s\n", "failed to retrieve system information");
203 return 0;
204 + }
205
206 - return sb.st_ctime;
207 + uptime = (uint64_t)time(NULL) - (uint64_t)sys.uptime;
208 + procstart = get_reaper_start_time_in_sec(pid);
209 + return uptime - procstart;
210 }
211
212 #define CPUALL_MAX_SIZE (BUF_RESERVE_SIZE / 2)
213 @@ -3539,7 +3673,7 @@ static int proc_stat_read(char *buf, size_t size, off_t offset,
214 if (sscanf(line, "cpu%9[^ ]", cpu_char) != 1) {
215 /* not a ^cpuN line containing a number N, just print it */
216 if (strncmp(line, "btime", 5) == 0)
217 - l = snprintf(cache, cache_size, "btime %ld\n", getreaperctime(fc->pid));
218 + l = snprintf(cache, cache_size, "btime %"PRIu64"\n", get_reaper_btime(fc->pid));
219 else
220 l = snprintf(cache, cache_size, "%s", line);
221 if (l < 0) {
222 @@ -3649,16 +3783,13 @@ err:
223 return rv;
224 }
225
226 -static long int getreaperage(pid_t pid)
227 -{
228 - long int ctime;
229 -
230 - ctime = getreaperctime(pid);
231 - if (ctime)
232 - return time(NULL) - ctime;
233 - return ctime;
234 -}
235 -
236 +/* This function retrieves the busy time of a group of tasks by looking at
237 + * cpuacct.usage. Unfortunately, this only makes sense when the container has
238 + * been given it's own cpuacct cgroup. If not, this function will take the busy
239 + * time of all other taks that do not actually belong to the container into
240 + * account as well. If someone has a clever solution for this please send a
241 + * patch!
242 + */
243 static unsigned long get_reaper_busy(pid_t task)
244 {
245 pid_t initpid = lookup_initpid_in_store(task);
246 @@ -3704,10 +3835,10 @@ static int proc_uptime_read(char *buf, size_t size, off_t offset,
247 {
248 struct fuse_context *fc = fuse_get_context();
249 struct file_info *d = (struct file_info *)fi->fh;
250 - long int reaperage = getreaperage(fc->pid);
251 - unsigned long int busytime = get_reaper_busy(fc->pid), idletime;
252 + unsigned long int busytime = get_reaper_busy(fc->pid);
253 char *cache = d->buf;
254 ssize_t total_len = 0;
255 + uint64_t idletime, reaperage;
256
257 #if RELOADTEST
258 iwashere();
259 @@ -3724,13 +3855,17 @@ static int proc_uptime_read(char *buf, size_t size, off_t offset,
260 return total_len;
261 }
262
263 - idletime = reaperage - busytime;
264 - if (idletime > reaperage)
265 - idletime = reaperage;
266 + reaperage = get_reaper_age(fc->pid);
267 + /* To understand why this is done, please read the comment to the
268 + * get_reaper_busy() function.
269 + */
270 + idletime = reaperage;
271 + if (reaperage >= busytime)
272 + idletime = reaperage - busytime;
273
274 - total_len = snprintf(d->buf, d->size, "%ld.0 %lu.0\n", reaperage, idletime);
275 - if (total_len < 0){
276 - perror("Error writing to cache");
277 + total_len = snprintf(d->buf, d->size, "%"PRIu64".00 %"PRIu64".00\n", reaperage, idletime);
278 + if (total_len < 0 || total_len >= d->size){
279 + lxcfs_error("%s\n", "failed to write to cache");
280 return 0;
281 }
282
283 --
284 2.11.0
285