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