Monitoring application wise background data usage.
[platform/core/connectivity/stc-manager.git] / src / helper / helper-cgroup.c
1 /*
2  * Copyright (c) 2016 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include "helper-cgroup.h"
18
19 #define RELEASE_AGENT   "release_agent"
20 #define NOTIFY_ON_RELEASE  "notify_on_release"
21
22 #define CGROUP_FILE_NAME "cgroup.procs"
23 #define MAX_PATH_LENGTH 512
24
25 static int read_uint(FILE *handler, uint32_t *out)
26 {
27         return fscanf(handler, "%u", out);
28 }
29
30 static int write_uint(FILE *handler, uint32_t number)
31 {
32         _cleanup_free_ char *digit_buf = NULL;
33         int ret;
34
35         ret = asprintf(&digit_buf, "%u\n", number);
36         ret_value_errno_msg_if(ret < 0, -ENOMEM, "asprintf failed\n");
37
38         ret = fputs(digit_buf, handler);
39         ret_value_errno_msg_if(ret == EOF, errno ? -errno : -EIO,
40                                "Fail to write file");
41
42         return 0;
43 }
44
45 static bool cgroup_is_exists(const char *cgroup_full_path)
46 {
47         struct stat stat_buf;
48         return stat(cgroup_full_path, &stat_buf) == 0;
49 }
50
51 static int cgroup_create(const char *cgroup_full_path)
52 {
53         if (mkdir(cgroup_full_path,
54                   S_IRUSR | S_IWUSR | S_IRGRP) < 0)
55                 return -errno;
56
57         return 0;
58 }
59
60 /*
61  * @desc place pid to cgroup.procs file
62  * @return 0 in case of success, errno value in case of failure
63  */
64 stc_error_e cgroup_write_pid_fullpath(const char *cgroup_full_path,
65                                       const int pid)
66 {
67         int ret;
68
69         if (pid <= 0) {
70                 STC_LOGE("try to write empty pid to %s", cgroup_full_path);
71                 return STC_ERROR_NO_DATA;
72         }
73
74         ret = cgroup_write_node_uint32(cgroup_full_path, CGROUP_FILE_NAME,
75                                        (uint32_t)pid);
76
77         ret_value_msg_if(ret < 0, STC_ERROR_FAIL,
78                          "Failed place all pid to cgroup %s", cgroup_full_path);
79         return STC_ERROR_NONE;
80 }
81
82 stc_error_e cgroup_write_pid(const char *cgroup_subsystem,
83                              const char *cgroup_name, const int pid)
84 {
85         char buf[MAX_PATH_LENGTH];
86         snprintf(buf, sizeof(buf), "%s/%s", cgroup_subsystem, cgroup_name);
87         return cgroup_write_pid_fullpath(buf, pid);
88 }
89
90 stc_error_e cgroup_write_pidtree(const char *cgroup_subsystem,
91                                  const char *cgroup_name, const int pid)
92 {
93         char buf[MAX_PATH_LENGTH];
94
95         /*/proc/%d/task/%d/children */
96         char child_buf[21 + MAX_DEC_SIZE(int) + MAX_DEC_SIZE(int)];
97         char pidbuf[MAX_DEC_SIZE(int)];
98         stc_error_e ret;
99
100         FILE *f;
101
102         snprintf(buf, sizeof(buf), "%s/%s", cgroup_subsystem, cgroup_name);
103         /* place parent */
104         ret = cgroup_write_pid_fullpath(buf, pid);
105         ret_value_msg_if(ret != STC_ERROR_NONE, ret,
106                          "Failed to put parent process %d into %s cgroup",
107                          pid, cgroup_name);
108
109         snprintf(child_buf, sizeof(child_buf), PROC_TASK_CHILDREN,
110                  pid, pid);
111         f = fopen(child_buf, "r");
112         ret_value_msg_if(!f, STC_ERROR_FAIL, "Failed to get child pids!");
113         while (fgets(pidbuf, sizeof(pidbuf), f) != NULL) {
114                 int child_pid = atoi(pidbuf);
115                 if (child_pid < 0) {
116                         STC_LOGE("Invalid child pid!");
117                         fclose(f);
118                         return STC_ERROR_FAIL;
119                 }
120                 stc_error_e ret = cgroup_write_pid_fullpath(buf, child_pid);
121                 if (ret != STC_ERROR_NONE) {
122                         STC_LOGE("Failed to put parent process %d into %s cgroup",
123                            pid, cgroup_name);
124                         fclose(f);
125                         return ret;
126                 }
127         }
128         fclose(f);
129         return STC_ERROR_NONE;
130 }
131
132 int cgroup_write_node_uint32(const char *cgroup_name,
133                              const char *file_name, uint32_t value)
134 {
135         char buf[MAX_PATH_LENGTH];
136         snprintf(buf, sizeof(buf), "%s/%s", cgroup_name, file_name);
137         STC_LOGD("cgroup_buf %s, value %d\n", buf, value);
138         return fwrite_uint(buf, value);
139 }
140
141 int cgroup_write_node_str(const char *cgroup_name,
142                           const char *file_name, const char *string)
143 {
144         char buf[MAX_PATH_LENGTH];
145         snprintf(buf, sizeof(buf), "%s/%s", cgroup_name, file_name);
146         STC_LOGD("cgroup_buf %s, string %s\n", buf, string);
147         return fwrite_str(buf, string);
148 }
149
150 int cgroup_read_node_uint32(const char *cgroup_name,
151                             const char *file_name, uint32_t *value)
152 {
153         char buf[MAX_PATH_LENGTH];
154         int ret;
155         snprintf(buf, sizeof(buf), "%s/%s", cgroup_name, file_name);
156         ret = fread_uint(buf, value);
157         STC_LOGD("cgroup_buf %s, value %d\n", buf, *value);
158         return ret;
159 }
160
161 int cgroup_make_subdir(const char *parentdir, const char *cgroup_name,
162                        bool *already)
163 {
164         char buf[MAX_PATH_LENGTH];
165         bool cgroup_exists;
166         int ret = 0;
167
168         if (parentdir)
169                 ret = snprintf(buf, sizeof(buf), "%s/%s",
170                                parentdir, cgroup_name);
171         else
172                 ret = snprintf(buf, sizeof(buf), "%s",
173                                cgroup_name);
174
175         ret_value_msg_if(ret > sizeof(buf), STC_ERROR_FAIL,
176                          "Not enought buffer size for %s%s",
177                          parentdir, cgroup_name);
178
179         cgroup_exists = cgroup_is_exists(buf);
180         if (!cgroup_exists) {
181                 bool cgroup_remount = false;
182
183                 if (parentdir && !strncmp(parentdir, DEFAULT_CGROUP,
184                                           sizeof(DEFAULT_CGROUP))) {
185                         ret = mount("tmpfs", DEFAULT_CGROUP, "tmpfs",
186                                     MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME,
187                                     "mode=755");
188                         if (ret < 0) {
189                                 STC_LOGE("Fail to RW mount cgroup directory. Can't make %s cgroup", cgroup_name);
190                                 return STC_ERROR_FAIL;
191                         }
192                         cgroup_remount = true;
193                 }
194
195                 ret = cgroup_create(buf);
196                 ret_value_msg_if(ret < 0, STC_ERROR_FAIL,
197                                  "Fail to create cgroup %s : err %d",
198                                  cgroup_name, errno);
199
200                 if (cgroup_remount) {
201                         ret = mount("tmpfs", DEFAULT_CGROUP, "tmpfs",
202                                     MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME|MS_RDONLY,
203                                     "mode=755");
204                         if (ret < 0)
205                                 STC_LOGD("Fail to RO mount");
206                 }
207         }
208
209         if (already)
210                 *already = cgroup_exists;
211
212         return STC_ERROR_NONE;
213 }
214
215 /* FIXME: tasks is not removed from tasks list */
216 int cgroup_remove_pid(const char *cgroup_subsystem, const char *cgroup_name,
217                       const int pid)
218 {
219         char cgroup_tasks_file_path[MAX_PATH_LENGTH];
220         FILE *handler = 0;
221         guint i = 0;
222         pid_t pid_for_read = 0;
223         GArray *pids = NULL;
224         guint pid_count = 0;;
225
226         snprintf(cgroup_tasks_file_path, sizeof(cgroup_tasks_file_path),
227                  "%s/%s/tasks", cgroup_subsystem, cgroup_name);
228
229         handler = fopen(cgroup_tasks_file_path, "r");
230         if (!handler) {
231                 STC_LOGE("Read file open failed");
232                 return -1;
233         }
234
235         pids = g_array_new(FALSE, FALSE, sizeof(pid_t));
236
237         while (read_uint(handler, (uint32_t *)&pid_for_read) >= 0) {
238                 if (pid_for_read != pid) {
239                         pids = g_array_append_val(pids, pid_for_read);
240                         ++pid_count;
241                 }
242         }
243
244         fclose(handler);
245
246         handler = fopen(cgroup_tasks_file_path, "w");
247         if (!handler) {
248                 STC_LOGE("Write file open failed");
249                 return -1;
250         }
251
252         for (i = 0; i < pid_count; i++)
253                 write_uint(handler, g_array_index(pids, pid_t, i));
254
255         fclose(handler);
256         g_array_free(pids, TRUE);
257         return 0;
258 }
259
260 int cgroup_set_release_agent(const char *cgroup_subsys,
261                              const char *release_agent)
262 {
263         _cleanup_free_ char *buf = NULL;
264         int r;
265
266         r = asprintf(&buf, "%s/%s", DEFAULT_CGROUP, cgroup_subsys);
267         if (r < 0)
268                 return -ENOMEM;
269
270         r = cgroup_write_node_str(buf, RELEASE_AGENT, release_agent);
271         if (r < 0)
272                 return r;
273
274         return cgroup_write_node_str(buf, NOTIFY_ON_RELEASE, "1");
275 }
276
277 void cgroup_init(void)
278 {
279         /* create stc cgroup directory */
280         cgroup_make_subdir(CGROUP_NETWORK, STC_CGROUP_NAME, NULL);
281
282         /* create background cgroup directory */
283         cgroup_make_subdir(STC_CGROUP_NETWORK, STC_BACKGROUND_CGROUP_NAME,
284                            NULL);
285
286         /* create foreground cgroup directory */
287         cgroup_make_subdir(STC_CGROUP_NETWORK, STC_FOREGROUND_CGROUP_NAME,
288                            NULL);
289 }